CVE-2022-2037: Other (NVD-CWE-Other)
value: Excessive Attack Surface in GitHub repository tooljet/tooljet prior to v1.16.0.
CWE description: NVD is only using a subset of CWE for mapping instead of the entire CWE, and the weakness type is not covered by that subset.
CVSS v3 base score: 8
Published: 2022-06-09T17:15Z
Repository: https://github.com/tooljet/tooljet
Language: TypeScript
Files changed: frontend/src/App/App.jsx, frontend/src/ConfirmationPage/ConfirmationPage.jsx, frontend/src/ConfirmationPage/OrganizationInvitationPage.jsx, frontend/src/ManageOrgUsers/ManageOrgUsers.jsx, frontend/src/_services/app.service.js, frontend/src/_services/user.service.js, server/src/controllers/app.controller.ts, server/src/controllers/organization_users.controller.ts, server/src/controllers/users.controller.ts, server/src/dto/accept-organization-invite.dto.ts, server/src/dto/user.dto.ts, server/src/services/auth.service.ts, server/src/services/email.service.ts, server/src/services/organization_users.service.ts, server/src/services/organizations.service.ts, server/src/services/users.service.ts, server/test/controllers/app.e2e-spec.ts, server/test/controllers/users.e2e-spec.ts
Commit date: 2022-06-02 15:20:51 +0530
Commit message: [BUG] Account level security issue (#3150)

* bug fixes

* changes

* changes for single workspace support

* added guards for signup route

* test cases fixes

* Workspace invite and user onboarding flow changes (#3190)

* invite user flow changes

* review comments

* cleanup

* testcase fix
Diff: diff --git a/frontend/src/App/App.jsx b/frontend/src/App/App.jsx
index 00e600ef..fd373c09 100644
--- a/frontend/src/App/App.jsx
+++ b/frontend/src/App/App.jsx
@@ -140,10 +140,24 @@ class App extends React.Component {
                     },
                   }}
                 />
               )}
             />
+            <Route
+              path="/invitations/:token/workspaces/:organizationToken"
+              render={(props) => (
+                <Redirect
+                  to={{
+                    pathname: '/confirm',
+                    state: {
+                      token: props.match.params.token,
+                      organizationToken: props.match.params.organizationToken,
+                    },
+                  }}
+                />
+              )}
+            />
             <Route path="/confirm" component={ConfirmationPage} />
             <Route
               path="/organization-invitations/:token"
               render={(props) => (
                 <Redirect
diff --git a/frontend/src/ConfirmationPage/ConfirmationPage.jsx b/frontend/src/ConfirmationPage/ConfirmationPage.jsx
index 7ba00652..fbfba735 100644
--- a/frontend/src/ConfirmationPage/ConfirmationPage.jsx
+++ b/frontend/src/ConfirmationPage/ConfirmationPage.jsx
@@ -1,7 +1,7 @@
 import React from 'react';
-import { userService } from '@/_services';
+import { appService } from '@/_services';
 import { toast } from 'react-hot-toast';
 
 class ConfirmationPage extends React.Component {
   constructor(props) {
     super(props);
@@ -21,11 +21,11 @@ class ConfirmationPage extends React.Component {
     return window.innerHeight - elementHeight;
   }
 
   setPassword = (e) => {
     e.preventDefault();
-    const token = this.props.location.state.token;
+    const { token, organizationToken } = this.props.location.state;
     const { password, organization, role, firstName, lastName, password_confirmation } = this.state;
     this.setState({ isLoading: true });
 
     if (!password || !password_confirmation || !password.trim() || !password_confirmation.trim()) {
       this.setState({ isLoading: false });
@@ -41,13 +41,14 @@ class ConfirmationPage extends React.Component {
         position: 'top-center',
       });
       return;
     }
 
-    userService
+    appService
       .setPasswordFromToken({
         token,
+        organizationToken,
         password,
         organization,
         role,
         firstName,
         lastName,
diff --git a/frontend/src/ConfirmationPage/OrganizationInvitationPage.jsx b/frontend/src/ConfirmationPage/OrganizationInvitationPage.jsx
index a77d883e..b32c113d 100644
--- a/frontend/src/ConfirmationPage/OrganizationInvitationPage.jsx
+++ b/frontend/src/ConfirmationPage/OrganizationInvitationPage.jsx
@@ -1,7 +1,7 @@
 import React from 'react';
-import { userService } from '@/_services';
+import { appService } from '@/_services';
 import { toast } from 'react-hot-toast';
 
 class OrganizationInvitationPage extends React.Component {
   constructor(props) {
     super(props);
@@ -40,25 +40,26 @@ class OrganizationInvitationPage extends React.Component {
         });
         return;
       }
     }
 
-    userService
+    appService
       .acceptInvite({
         token,
         password,
       })
-      .then(() => {
+      .then((response) => {
         this.setState({ isLoading: false });
-        toast.success(`Added to the workspace${isSetPassword ? ' and password has been set ' : ' '}successfully.`, {
-          position: 'top-center',
+        response.json().then((data) => {
+          if (!response.ok) {
+            return toast.error(data?.message || 'Error while setting up your account.', { position: 'top-center' });
+          }
+          toast.success(`Added to the workspace${isSetPassword ? ' and password has been set ' : ' '}successfully.`, {
+            position: 'top-center',
+          });
+          this.props.history.push('/login');
         });
-        this.props.history.push('/login');
-      })
-      .catch(({ error }) => {
-        this.setState({ isLoading: false });
-        toast.error(error, { position: 'top-center' });
       });
   };
 
   render() {
     const { isLoading } = this.state;
@@ -71,11 +72,11 @@ class OrganizationInvitationPage extends React.Component {
               <img src="/assets/images/logo-color.svg" height="30" alt="" />
             </a>
           </div>
           <form className="card card-md" action="." method="get" autoComplete="off">
             <div className="card-body">
-              {!this.single_organization && (
+              {!this.single_organization ? (
                 <>
                   <h2 className="card-title text-center mb-2">Already have an account?</h2>
                   <div className="mb-3">
                     <button
                       className={`btn mt-2 btn-primary w-100 ${isLoading ? ' btn-loading' : ''}`}
@@ -83,57 +84,55 @@ class OrganizationInvitationPage extends React.Component {
                       disabled={isLoading}
                     >
                       Accept invite
                     </button>
                   </div>
-                  <div className="org-invite-or">
-                    <h2>
-                      <span>OR</span>
-                    </h2>
+                </>
+              ) : (
+                <>
+                  <h2 className="card-title text-center mb-4">Set up your account</h2>
+                  <div className="mb-3">
+                    <label className="form-label">Password</label>
+                    <div className="input-group input-group-flat">
+                      <input
+                        onChange={this.handleChange}
+                        name="password"
+                        type="password"
+                        className="form-control"
+                        autoComplete="off"
+                      />
+                      <span className="input-group-text"></span>
+                    </div>
+                  </div>
+                  <div className="mb-3">
+                    <label className="form-label">Confirm Password</label>
+                    <div className="input-group input-group-flat">
+                      <input
+                        onChange={this.handleChange}
+                        name="password_confirmation"
+                        type="password"
+                        className="form-control"
+                        autoComplete="off"
+                      />
+                      <span className="input-group-text"></span>
+                    </div>
+                  </div>
+                  <div className="form-footer">
+                    <p>
+                      By clicking the button below, you agree to our{' '}
+                      <a href="https://tooljet.io/terms">Terms and Conditions</a>.
+                    </p>
+                    <button
+                      className={`btn mt-2 btn-primary w-100 ${isLoading ? ' btn-loading' : ''}`}
+                      onClick={(e) => this.acceptInvite(e, true)}
+                      disabled={isLoading}
+                    >
+                      Finish account setup and accept invite
+                    </button>
                   </div>
                 </>
               )}
-              <h2 className="card-title text-center mb-4">Set up your account</h2>
-              <div className="mb-3">
-                <label className="form-label">Password</label>
-                <div className="input-group input-group-flat">
-                  <input
-                    onChange={this.handleChange}
-                    name="password"
-                    type="password"
-                    className="form-control"
-                    autoComplete="off"
-                  />
-                  <span className="input-group-text"></span>
-                </div>
-              </div>
-              <div className="mb-3">
-                <label className="form-label">Confirm Password</label>
-                <div className="input-group input-group-flat">
-                  <input
-                    onChange={this.handleChange}
-                    name="password_confirmation"
-                    type="password"
-                    className="form-control"
-                    autoComplete="off"
-                  />
-                  <span className="input-group-text"></span>
-                </div>
-              </div>
-              <div className="form-footer">
-                <p>
-                  By clicking the button below, you agree to our{' '}
-                  <a href="https://tooljet.io/terms">Terms and Conditions</a>.
-                </p>
-                <button
-                  className={`btn mt-2 btn-primary w-100 ${isLoading ? ' btn-loading' : ''}`}
-                  onClick={(e) => this.acceptInvite(e, true)}
-                  disabled={isLoading}
-                >
-                  Finish account setup and accept invite
-                </button>
-              </div>
             </div>
           </form>
         </div>
       </div>
     );
diff --git a/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx b/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx
index b78eb6df..2d458ec2 100644
--- a/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx
+++ b/frontend/src/ManageOrgUsers/ManageOrgUsers.jsx
@@ -1,10 +1,9 @@
 import React from 'react';
 import { authenticationService, organizationService, organizationUserService } from '@/_services';
 import { Header } from '@/_components';
 import { toast } from 'react-hot-toast';
-import { history } from '@/_helpers';
 import { CopyToClipboard } from 'react-copy-to-clipboard';
 import ReactTooltip from 'react-tooltip';
 
 class ManageOrgUsers extends React.Component {
   constructor(props) {
@@ -24,11 +23,10 @@ class ManageOrgUsers extends React.Component {
 
     this.tableRef = React.createRef(null);
   }
 
   validateEmail(email) {
-    console.log(email);
     const re =
       /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
     return re.test(String(email).toLowerCase());
   }
 
@@ -166,15 +164,10 @@ class ManageOrgUsers extends React.Component {
     } else {
       this.setState({ creatingUser: false, showNewUserForm: true });
     }
   };
 
-  logout = () => {
-    authenticationService.logout();
-    history.push('/login');
-  };
-
   generateInvitationURL = (user) => window.location.origin + '/organization-invitations/' + user.invitation_token;
 
   invitationLinkCopyHandler = () => {
     toast.success('Invitation URL copied', {
       position: 'bottom-right',
diff --git a/frontend/src/_services/app.service.js b/frontend/src/_services/app.service.js
index 194cd750..309524fa 100644
--- a/frontend/src/_services/app.service.js
+++ b/frontend/src/_services/app.service.js
@@ -17,10 +17,12 @@ export const appService = {
   getAppUsers,
   createAppUser,
   setVisibility,
   setMaintenance,
   setSlug,
+  setPasswordFromToken,
+  acceptInvite,
 };
 
 function getConfig() {
   const requestOptions = { method: 'GET', headers: authHeader() };
   return fetch(`${config.apiUrl}/config`, requestOptions).then(handleResponse);
@@ -124,5 +126,30 @@ function setMaintenance(appId, value) {
 
 function setSlug(appId, slug) {
   const requestOptions = { method: 'PUT', headers: authHeader(), body: JSON.stringify({ app: { slug: slug } }) };
   return fetch(`${config.apiUrl}/apps/${appId}`, requestOptions).then(handleResponse);
 }
+
+function setPasswordFromToken({ token, password, organization, role, firstName, lastName, organizationToken }) {
+  const body = {
+    token,
+    organizationToken,
+    password,
+    organization,
+    role,
+    first_name: firstName,
+    last_name: lastName,
+  };
+
+  const requestOptions = { method: 'POST', headers: authHeader(), body: JSON.stringify(body) };
+  return fetch(`${config.apiUrl}/set-password-from-token`, requestOptions).then(handleResponse);
+}
+
+function acceptInvite({ token, password }) {
+  const body = {
+    token,
+    password,
+  };
+
+  const requestOptions = { method: 'POST', headers: authHeader(), body: JSON.stringify(body) };
+  return fetch(`${config.apiUrl}/accept-invite`, requestOptions);
+}
diff --git a/frontend/src/_services/user.service.js b/frontend/src/_services/user.service.js
index 481a489b..f5a029d9 100644
--- a/frontend/src/_services/user.service.js
+++ b/frontend/src/_services/user.service.js
@@ -3,14 +3,12 @@ import { authHeader, handleResponse } from '@/_helpers';
 
 export const userService = {
   getAll,
   createUser,
   deleteUser,
-  setPasswordFromToken,
   updateCurrentUser,
   changePassword,
-  acceptInvite,
   getAvatar,
   updateAvatar,
 };
 
 function getAll() {
@@ -51,34 +49,10 @@ function createUser(first_name, last_name, email, role) {
 function deleteUser(id) {
   const requestOptions = { method: 'DELETE', headers: authHeader(), body: JSON.stringify({}) };
   return fetch(`${config.apiUrl}/users/${id}`, requestOptions).then(handleResponse);
 }
 
-function setPasswordFromToken({ token, password, organization, role, firstName, lastName }) {
-  const body = {
-    token,
-    password,
-    organization,
-    role,
-    first_name: firstName,
-    last_name: lastName,
-  };
-
-  const requestOptions = { method: 'POST', headers: authHeader(), body: JSON.stringify(body) };
-  return fetch(`${config.apiUrl}/users/set_password_from_token`, requestOptions).then(handleResponse);
-}
-
-function acceptInvite({ token, password }) {
-  const body = {
-    token,
-    password,
-  };
-
-  const requestOptions = { method: 'POST', headers: authHeader(), body: JSON.stringify(body) };
-  return fetch(`${config.apiUrl}/users/accept-invite`, requestOptions).then(handleResponse);
-}
-
 function updateCurrentUser(firstName, lastName) {
   const body = { first_name: firstName, last_name: lastName };
   const requestOptions = { method: 'PATCH', headers: authHeader(), body: JSON.stringify(body) };
   return fetch(`${config.apiUrl}/users/update`, requestOptions).then(handleResponse);
 }
diff --git a/server/src/controllers/app.controller.ts b/server/src/controllers/app.controller.ts
index bd625fc5..b9a06a3f 100644
--- a/server/src/controllers/app.controller.ts
+++ b/server/src/controllers/app.controller.ts
@@ -1,10 +1,14 @@
 import { Controller, Get, Request, Post, UseGuards, Body, Param, BadRequestException } from '@nestjs/common';
 import { User } from 'src/decorators/user.decorator';
 import { JwtAuthGuard } from '../../src/modules/auth/jwt-auth.guard';
 import { AppAuthenticationDto, AppForgotPasswordDto, AppPasswordResetDto } from '@dto/app-authentication.dto';
 import { AuthService } from '../services/auth.service';
+import { MultiOrganizationGuard } from 'src/modules/auth/multi-organization.guard';
+import { SignupDisableGuard } from 'src/modules/auth/signup-disable.guard';
+import { CreateUserDto } from '@dto/user.dto';
+import { AcceptInviteDto } from '@dto/accept-organization-invite.dto';
 
 @Controller()
 export class AppController {
   constructor(private authService: AuthService) {}
 
@@ -20,10 +24,24 @@ export class AppController {
       throw new BadRequestException();
     }
     return await this.authService.switchOrganization(organizationId, user);
   }
 
+  @UseGuards(MultiOrganizationGuard, SignupDisableGuard)
+  @Post('set-password-from-token')
+  async create(@Body() userCreateDto: CreateUserDto) {
+    await this.authService.setupAccountFromInvitationToken(userCreateDto);
+    return {};
+  }
+
+  @Post('accept-invite')
+  async acceptInvite(@Body() acceptInviteDto: AcceptInviteDto) {
+    await this.authService.acceptOrganizationInvite(acceptInviteDto);
+    return {};
+  }
+
+  @UseGuards(MultiOrganizationGuard, SignupDisableGuard)
   @Post('signup')
   async signup(@Body() appAuthDto: AppAuthenticationDto) {
     return this.authService.signup(appAuthDto.email);
   }
 
diff --git a/server/src/controllers/organization_users.controller.ts b/server/src/controllers/organization_users.controller.ts
index df769cf1..ba72c8fe 100644
--- a/server/src/controllers/organization_users.controller.ts
+++ b/server/src/controllers/organization_users.controller.ts
@@ -6,21 +6,25 @@ import { AppAbility } from 'src/modules/casl/casl-ability.factory';
 import { PoliciesGuard } from 'src/modules/casl/policies.guard';
 import { CheckPolicies } from 'src/modules/casl/check_policies.decorator';
 import { User as UserEntity } from 'src/entities/user.entity';
 import { User } from 'src/decorators/user.decorator';
 import { InviteNewUserDto } from '../dto/invite-new-user.dto';
+import { OrganizationsService } from '@services/organizations.service';
 
 @Controller('organization_users')
 export class OrganizationUsersController {
-  constructor(private organizationUsersService: OrganizationUsersService) {}
+  constructor(
+    private organizationUsersService: OrganizationUsersService,
+    private organizationsService: OrganizationsService
+  ) {}
 
   // Endpoint for inviting new organization users
   @UseGuards(JwtAuthGuard, PoliciesGuard)
   @CheckPolicies((ability: AppAbility) => ability.can('inviteUser', UserEntity))
   @Post()
   async create(@User() user, @Body() inviteNewUserDto: InviteNewUserDto) {
-    const result = await this.organizationUsersService.inviteNewUser(user, inviteNewUserDto);
+    const result = await this.organizationsService.inviteNewUser(user, inviteNewUserDto);
     return decamelizeKeys({ users: result });
   }
 
   @UseGuards(JwtAuthGuard, PoliciesGuard)
   @CheckPolicies((ability: AppAbility) => ability.can('archiveUser', UserEntity))
diff --git a/server/src/controllers/users.controller.ts b/server/src/controllers/users.controller.ts
index 540fd06f..5d4db2ec 100644
--- a/server/src/controllers/users.controller.ts
+++ b/server/src/controllers/users.controller.ts
@@ -1,34 +1,18 @@
-import { Body, Controller, Post, Patch, UseGuards, UseInterceptors, Req, UploadedFile } from '@nestjs/common';
+import { Body, Controller, Post, Patch, UseGuards, UseInterceptors, UploadedFile } from '@nestjs/common';
 import { Express } from 'express';
 import { FileInterceptor } from '@nestjs/platform-express';
 import { JwtAuthGuard } from 'src/modules/auth/jwt-auth.guard';
 import { PasswordRevalidateGuard } from 'src/modules/auth/password-revalidate.guard';
 import { UsersService } from 'src/services/users.service';
 import { User } from 'src/decorators/user.decorator';
-import { SignupDisableGuard } from 'src/modules/auth/signup-disable.guard';
-import { CreateUserDto, UpdateUserDto } from '@dto/user.dto';
-import { AcceptInviteDto } from '@dto/accept-organization-invite.dto';
-import { MultiOrganizationGuard } from 'src/modules/auth/multi-organization.guard';
+import { UpdateUserDto } from '@dto/user.dto';
 
 @Controller('users')
 export class UsersController {
   constructor(private usersService: UsersService) {}
 
-  @UseGuards(MultiOrganizationGuard, SignupDisableGuard)
-  @Post('set_password_from_token')
-  async create(@Body() userCreateDto: CreateUserDto) {
-    await this.usersService.setupAccountFromInvitationToken(userCreateDto);
-    return {};
-  }
-
-  @Post('accept-invite')
-  async acceptInvite(@Body() acceptInviteDto: AcceptInviteDto) {
-    await this.usersService.acceptOrganizationInvite(acceptInviteDto);
-    return {};
-  }
-
   @UseGuards(JwtAuthGuard)
   @Patch('update')
   async update(@User() user, @Body() updateUserDto: UpdateUserDto) {
     const { first_name: firstName, last_name: lastName } = updateUserDto;
     await this.usersService.update(user.id, { firstName, lastName });
@@ -40,12 +24,12 @@ export class UsersController {
   }
 
   @Post('avatar')
   @UseGuards(JwtAuthGuard)
   @UseInterceptors(FileInterceptor('file'))
-  async addAvatar(@Req() req, @UploadedFile() file: Express.Multer.File) {
-    return this.usersService.addAvatar(req.user.id, file.buffer, file.originalname);
+  async addAvatar(@User() user, @UploadedFile() file: Express.Multer.File) {
+    return this.usersService.addAvatar(user.id, file.buffer, file.originalname);
   }
 
   @UseGuards(JwtAuthGuard, PasswordRevalidateGuard)
   @Patch('change_password')
   async changePassword(@User() user, @Body('newPassword') newPassword) {
diff --git a/server/src/dto/accept-organization-invite.dto.ts b/server/src/dto/accept-organization-invite.dto.ts
index 5ba68d32..86d83489 100644
--- a/server/src/dto/accept-organization-invite.dto.ts
+++ b/server/src/dto/accept-organization-invite.dto.ts
@@ -1,6 +1,6 @@
-import { IsString, IsOptional, IsNotEmpty } from 'class-validator';
+import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
 
 export class AcceptInviteDto {
   @IsString()
   @IsOptional()
   @IsNotEmpty()
diff --git a/server/src/dto/user.dto.ts b/server/src/dto/user.dto.ts
index e2e3f621..8d45c006 100644
--- a/server/src/dto/user.dto.ts
+++ b/server/src/dto/user.dto.ts
@@ -24,10 +24,15 @@ export class CreateUserDto {
 
   @IsString()
   @IsNotEmpty()
   password: string;
 
+  @IsString()
+  @IsOptional()
+  @IsNotEmpty()
+  organizationToken: string;
+
   @IsString()
   @IsNotEmpty()
   token: string;
 
   @IsString()
diff --git a/server/src/services/auth.service.ts b/server/src/services/auth.service.ts
index 5c1a1edb..5505d5e2 100644
--- a/server/src/services/auth.service.ts
+++ b/server/src/services/auth.service.ts
@@ -1,22 +1,39 @@
-import { Injectable, NotAcceptableException, NotFoundException, UnauthorizedException } from '@nestjs/common';
+import {
+  BadRequestException,
+  Injectable,
+  NotAcceptableException,
+  NotFoundException,
+  UnauthorizedException,
+} from '@nestjs/common';
 import { UsersService } from './users.service';
 import { OrganizationsService } from './organizations.service';
 import { JwtService } from '@nestjs/jwt';
 import { User } from '../entities/user.entity';
 import { OrganizationUsersService } from './organization_users.service';
 import { EmailService } from './email.service';
 import { decamelizeKeys } from 'humps';
 import { Organization } from 'src/entities/organization.entity';
 import { ConfigService } from '@nestjs/config';
 import { SSOConfigs } from 'src/entities/sso_config.entity';
+import { InjectRepository } from '@nestjs/typeorm';
+import { Repository } from 'typeorm';
+import { OrganizationUser } from 'src/entities/organization_user.entity';
+import { CreateUserDto } from '@dto/user.dto';
+import { AcceptInviteDto } from '@dto/accept-organization-invite.dto';
 const bcrypt = require('bcrypt');
 const uuid = require('uuid');
 
 @Injectable()
 export class AuthService {
   constructor(
+    @InjectRepository(User)
+    private usersRepository: Repository<User>,
+    @InjectRepository(OrganizationUser)
+    private organizationUsersRepository: Repository<OrganizationUser>,
+    @InjectRepository(Organization)
+    private organizationsRepository: Repository<Organization>,
     private usersService: UsersService,
     private jwtService: JwtService,
     private organizationsService: OrganizationsService,
     private organizationUsersService: OrganizationUsersService,
     private emailService: EmailService,
@@ -30,12 +47,12 @@ export class AuthService {
     } catch (err) {
       return null;
     }
   }
 
-  private async validateUser(email: string, password: string, organisationId?: string): Promise<User> {
-    const user = await this.usersService.findByEmail(email, organisationId);
+  private async validateUser(email: string, password: string, organizationId?: string): Promise<User> {
+    const user = await this.usersService.findByEmail(email, organizationId);
 
     if (!user) return null;
 
     const isVerified = await bcrypt.compare(password, user.password);
 
@@ -167,14 +184,23 @@ export class AuthService {
     }
   }
 
   async signup(email: string) {
     const existingUser = await this.usersService.findByEmail(email);
-    if (existingUser?.invitationToken || existingUser?.organizationUsers?.some((ou) => ou.status === 'active')) {
+    if (existingUser?.organizationUsers?.some((ou) => ou.status === 'active')) {
       throw new NotAcceptableException('Email already exists');
     }
 
+    if (existingUser?.invitationToken) {
+      await this.emailService.sendWelcomeEmail(
+        existingUser.email,
+        existingUser.firstName,
+        existingUser.invitationToken
+      );
+      return;
+    }
+
     let organization: Organization;
     // Check if the configs allows user signups
     if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') === 'true') {
       // Single organization checking if organization exist
       organization = await this.organizationsService.getSingleOrganization();
@@ -213,6 +239,122 @@ export class AuthService {
         password,
         forgotPasswordToken: null,
       });
     }
   }
+
+  async setupAccountFromInvitationToken(userCreateDto: CreateUserDto) {
+    const {
+      organization,
+      password,
+      token,
+      role,
+      first_name: firstName,
+      last_name: lastName,
+      organizationToken,
+    } = userCreateDto;
+
+    if (!token) {
+      throw new BadRequestException('Invalid token');
+    }
+
+    const user: User = await this.usersRepository.findOne({ where: { invitationToken: token } });
+
+    if (!user?.organizationUsers) {
+      throw new BadRequestException('Invalid invitation link');
+    }
+    const organizationUser: OrganizationUser = user.organizationUsers.find(
+      (ou) => ou.organizationId === user.defaultOrganizationId
+    );
+
+    if (!organizationUser) {
+      throw new BadRequestException('Invalid invitation link');
+    }
+
+    await this.usersRepository.save(
+      Object.assign(user, {
+        firstName,
+        lastName,
+        password,
+        role,
+        invitationToken: null,
+      })
+    );
+
+    await this.organizationUsersRepository.save(
+      Object.assign(organizationUser, {
+        invitationToken: null,
+        status: 'active',
+      })
+    );
+
+    if (organization) {
+      await this.organizationsRepository.update(user.defaultOrganizationId, {
+        name: organization,
+      });
+    }
+
+    if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') !== 'true' && organizationToken) {
+      const organizationUser = await this.organizationUsersRepository.findOne({
+        where: { invitationToken: organizationToken },
+      });
+
+      if (organizationUser) {
+        await this.organizationUsersRepository.save(
+          Object.assign(organizationUser, {
+            invitationToken: null,
+            status: 'active',
+          })
+        );
+      }
+    }
+  }
+
+  async acceptOrganizationInvite(acceptInviteDto: AcceptInviteDto) {
+    const { password, token } = acceptInviteDto;
+
+    if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') === 'true' && !password) {
+      throw new BadRequestException('Please enter password');
+    }
+    const organizationUser = await this.organizationUsersRepository.findOne({
+      where: { invitationToken: token },
+      relations: ['user'],
+    });
+
+    if (!organizationUser?.user) {
+      throw new BadRequestException('Invalid invitation link');
+    }
+    const user: User = organizationUser.user;
+
+    if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') !== 'true' && user.invitationToken) {
+      // User sign up link send - not activated account
+      this.emailService
+        .sendWelcomeEmail(
+          user.email,
+          `${user.firstName} ${user.lastName}`,
+          user.invitationToken,
+          organizationUser.invitationToken
+        )
+        .catch((err) => console.error('Error while sending welcome mail', err));
+      throw new UnauthorizedException(
+        'User not exist in the workspace, Please setup your account using link shared via email'
+      );
+    }
+
+    if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') === 'true') {
+      // set new password
+      await this.usersRepository.save(
+        Object.assign(user, {
+          ...(password ? { password } : {}),
+          invitationToken: null,
+        })
+      );
+    }
+
+    await this.organizationUsersRepository.save(
+      Object.assign(organizationUser, {
+        invitationToken: null,
+        status: 'active',
+      })
+    );
+  }
 }
diff --git a/server/src/services/email.service.ts b/server/src/services/email.service.ts
index e6f90229..90457124 100644
--- a/server/src/services/email.service.ts
+++ b/server/src/services/email.service.ts
@@ -51,21 +51,37 @@ export class EmailService {
 
   stripTrailingSlash(hostname: string) {
     return hostname?.endsWith('/') ? hostname.slice(0, -1) : hostname;
   }
 
-  async sendWelcomeEmail(to: string, name: string, invitationtoken: string) {
+  async sendWelcomeEmail(
+    to: string,
+    name: string,
+    invitationtoken: string,
+    organizationInvitationToken?: string,
+    organizationName?: string,
+    sender?: string
+  ) {
     const subject = 'Welcome to ToolJet';
-    const inviteUrl = `${this.TOOLJET_HOST}/invitations/${invitationtoken}`;
+    const inviteUrl = `${this.TOOLJET_HOST}/invitations/${invitationtoken}${
+      organizationInvitationToken ? `/workspaces/${organizationInvitationToken}` : ''
+    }`;
     const html = `
       <!DOCTYPE html>
       <html>
         <head>
           <meta content='text/html; charset=UTF-8' http-equiv='Content-Type' />
         </head>
         <body>
           <p>Hi ${name || ''},</p>
+          ${
+            organizationInvitationToken && sender && organizationName
+              ? `<span>
+              ${sender} has invited you to use ToolJet workspace: ${organizationName}.
+            </span>`
+              : ''
+          }
           <span>
             Please use the link below to set up your account and get started.
           </span>
           <br>
           <a href="${inviteUrl}">${inviteUrl}</a>
@@ -84,11 +100,11 @@ export class EmailService {
   async sendOrganizationUserWelcomeEmail(
     to: string,
     name: string,
     sender: string,
     invitationtoken: string,
-    organisationName: string
+    organizationName: string
   ) {
     const subject = 'Welcome to ToolJet';
     const inviteUrl = `${this.TOOLJET_HOST}/organization-invitations/${invitationtoken}`;
     const html = `
       <!DOCTYPE html>
@@ -98,11 +114,11 @@ export class EmailService {
         </head>
         <body>
           <p>Hi ${name || ''},</p>
           <br>
           <span>
-          ${sender} has invited you to use ToolJet workspace ${organisationName}. Use the link below to set up your account and get started.
+          ${sender} has invited you to use ToolJet workspace: ${organizationName}. Use the link below to set up your account and get started.
           </span>
           <br>
           <a href="${inviteUrl}">${inviteUrl}</a>
           <br>
           <br>
diff --git a/server/src/services/organization_users.service.ts b/server/src/services/organization_users.service.ts
index d27f0f7e..53a2998a 100644
--- a/server/src/services/organization_users.service.ts
+++ b/server/src/services/organization_users.service.ts
@@ -6,11 +6,10 @@ import { UsersService } from 'src/services/users.service';
 import { OrganizationUser } from 'src/entities/organization_user.entity';
 import { BadRequestException } from '@nestjs/common';
 import { EmailService } from './email.service';
 import { Organization } from 'src/entities/organization.entity';
 import { GroupPermission } from 'src/entities/group_permission.entity';
-import { InviteNewUserDto } from '@dto/invite-new-user.dto';
 const uuid = require('uuid');
 
 @Injectable()
 export class OrganizationUsersService {
   constructor(
@@ -22,50 +21,10 @@ export class OrganizationUsersService {
 
   async findOrganization(id: string): Promise<OrganizationUser> {
     return await this.organizationUsersRepository.findOne({ where: { id } });
   }
 
-  async inviteNewUser(currentUser: User, inviteNewUserDto: InviteNewUserDto): Promise<OrganizationUser> {
-    const userParams = <User>{
-      firstName: inviteNewUserDto.first_name,
-      lastName: inviteNewUserDto.last_name,
-      email: inviteNewUserDto.email,
-    };
-
-    let user = await this.usersService.findByEmail(userParams.email);
-
-    if (user?.organizationUsers?.some((ou) => ou.organizationId === currentUser.organizationId)) {
-      throw new BadRequestException('User with such email already exists.');
-    }
-
-    if (user?.invitationToken) {
-      // user sign up not completed, name will be empty - updating name
-      await this.usersService.update(user.id, { firstName: userParams.firstName, lastName: userParams.lastName });
-    }
-
-    user = await this.usersService.create(userParams, currentUser.organizationId, ['all_users'], user);
-
-    const currentOrganization: Organization = (
-      await this.organizationUsersRepository.findOne({
-        where: { userId: currentUser.id, organizationId: currentUser.organizationId },
-        relations: ['organization'],
-      })
-    )?.organization;
-
-    const organizationUser: OrganizationUser = await this.create(user, currentOrganization, true);
-
-    await this.emailService.sendOrganizationUserWelcomeEmail(
-      user.email,
-      user.firstName,
-      currentUser.firstName,
-      organizationUser.invitationToken,
-      currentOrganization.name
-    );
-
-    return organizationUser;
-  }
-
   async create(user: User, organization: Organization, isInvite?: boolean): Promise<OrganizationUser> {
     return await this.organizationUsersRepository.save(
       this.organizationUsersRepository.create({
         user,
         organization,
diff --git a/server/src/services/organizations.service.ts b/server/src/services/organizations.service.ts
index 20cf667a..7e9cf75b 100644
--- a/server/src/services/organizations.service.ts
+++ b/server/src/services/organizations.service.ts
@@ -5,14 +5,17 @@ import { Organization } from 'src/entities/organization.entity';
 import { SSOConfigs } from 'src/entities/sso_config.entity';
 import { User } from 'src/entities/user.entity';
 import { cleanObject } from 'src/helpers/utils.helper';
 import { createQueryBuilder, Repository } from 'typeorm';
 import { OrganizationUser } from '../entities/organization_user.entity';
+import { EmailService } from './email.service';
 import { EncryptionService } from './encryption.service';
 import { GroupPermissionsService } from './group_permissions.service';
 import { OrganizationUsersService } from './organization_users.service';
 import { UsersService } from './users.service';
+import { InviteNewUserDto } from '@dto/invite-new-user.dto';
+import { ConfigService } from '@nestjs/config';
 
 @Injectable()
 export class OrganizationsService {
   constructor(
     @InjectRepository(Organization)
@@ -24,11 +27,13 @@ export class OrganizationsService {
     @InjectRepository(GroupPermission)
     private groupPermissionsRepository: Repository<GroupPermission>,
     private usersService: UsersService,
     private organizationUserService: OrganizationUsersService,
     private groupPermissionService: GroupPermissionsService,
-    private encryptionService: EncryptionService
+    private encryptionService: EncryptionService,
+    private emailService: EmailService,
+    private configService: ConfigService
   ) {}
 
   async create(name: string, user?: User): Promise<Organization> {
     const organization = await this.organizationsRepository.save(
       this.organizationsRepository.create({
@@ -306,6 +311,85 @@ export class OrganizationsService {
       relations: ['organization'],
     });
     await this.decryptSecret(result?.configs);
     return result;
   }
+
+  async inviteNewUser(currentUser: User, inviteNewUserDto: InviteNewUserDto): Promise<OrganizationUser> {
+    const userParams = <User>{
+      firstName: inviteNewUserDto.first_name,
+      lastName: inviteNewUserDto.last_name,
+      email: inviteNewUserDto.email,
+    };
+
+    let user = await this.usersService.findByEmail(userParams.email);
+    let defaultOrganisation: Organization,
+      shouldSendWelcomeMail = false;
+
+    if (user?.organizationUsers?.some((ou) => ou.organizationId === currentUser.organizationId)) {
+      throw new BadRequestException('User with such email already exists.');
+    }
+
+    if (user?.invitationToken) {
+      // user sign up not completed, name will be empty - updating name
+      await this.usersService.update(user.id, { firstName: userParams.firstName, lastName: userParams.lastName });
+    }
+
+    if (!user && this.configService.get<string>('DISABLE_MULTI_WORKSPACE') !== 'true') {
+      // User not exist
+      shouldSendWelcomeMail = true;
+      // Create default organization
+      defaultOrganisation = await this.create('Untitled workspace');
+    }
+    user = await this.usersService.create(
+      userParams,
+      currentUser.organizationId,
+      ['all_users'],
+      user,
+      true,
+      defaultOrganisation?.id
+    );
+
+    if (defaultOrganisation) {
+      // Setting up default organization
+      await this.organizationUserService.create(user, defaultOrganisation, true);
+      await this.usersService.attachUserGroup(['all_users', 'admin'], defaultOrganisation.id, user.id);
+    }
+
+    const currentOrganization: Organization = (
+      await this.organizationUsersRepository.findOne({
+        where: { userId: currentUser.id, organizationId: currentUser.organizationId },
+        relations: ['organization'],
+      })
+    )?.organization;
+
+    const organizationUser: OrganizationUser = await this.organizationUserService.create(
+      user,
+      currentOrganization,
+      true
+    );
+
+    if (shouldSendWelcomeMail) {
+      this.emailService
+        .sendWelcomeEmail(
+          user.email,
+          user.firstName,
+          user.invitationToken,
+          organizationUser.invitationToken,
+          currentOrganization.name,
+          `${currentUser.firstName} ${currentUser.lastName}`
+        )
+        .catch((err) => console.error('Error while sending welcome mail', err));
+    } else {
+      this.emailService
+        .sendOrganizationUserWelcomeEmail(
+          user.email,
+          user.firstName,
+          `${currentUser.firstName} ${currentUser.lastName}`,
+          organizationUser.invitationToken,
+          currentOrganization.name
+        )
+        .catch((err) => console.error('Error while sending welcome mail', err));
+    }
+    return organizationUser;
+  }
 }
diff --git a/server/src/services/users.service.ts b/server/src/services/users.service.ts
index 5de57396..92dfbe58 100644
--- a/server/src/services/users.service.ts
+++ b/server/src/services/users.service.ts
@@ -1,19 +1,17 @@
 import { Injectable, InternalServerErrorException } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { User } from '../entities/user.entity';
 import { FilesService } from '../services/files.service';
-import { Organization } from 'src/entities/organization.entity';
 import { App } from 'src/entities/app.entity';
 import { Connection, createQueryBuilder, EntityManager, getManager, getRepository, In, Repository } from 'typeorm';
 import { OrganizationUser } from '../entities/organization_user.entity';
 import { AppGroupPermission } from 'src/entities/app_group_permission.entity';
 import { UserGroupPermission } from 'src/entities/user_group_permission.entity';
 import { GroupPermission } from 'src/entities/group_permission.entity';
 import { BadRequestException } from '@nestjs/common';
 import { cleanObject } from 'src/helpers/utils.helper';
-import { CreateUserDto } from '@dto/user.dto';
 import { CreateFileDto } from '@dto/create-file.dto';
 const uuid = require('uuid');
 const bcrypt = require('bcrypt');
 
 @Injectable()
@@ -23,12 +21,10 @@ export class UsersService {
     private connection: Connection,
     @InjectRepository(User)
     private usersRepository: Repository<User>,
     @InjectRepository(OrganizationUser)
     private organizationUsersRepository: Repository<OrganizationUser>,
-    @InjectRepository(Organization)
-    private organizationsRepository: Repository<Organization>,
     @InjectRepository(App)
     private appsRepository: Repository<App>
   ) {}
 
   async findOne(id: string): Promise<User> {
@@ -63,11 +59,12 @@ export class UsersService {
   async create(
     userParams: any,
     organizationId: string,
     groups?: string[],
     existingUser?: User,
-    isInvite?: boolean
+    isInvite?: boolean,
+    defaultOrganizationId?: string
   ): Promise<User> {
     const password = uuid.v4();
 
     const { email, firstName, lastName } = userParams;
     let user: User;
@@ -78,28 +75,27 @@ export class UsersService {
           email,
           firstName,
           lastName,
           password,
           invitationToken: isInvite ? uuid.v4() : null,
-          defaultOrganizationId: organizationId,
+          defaultOrganizationId: defaultOrganizationId || organizationId,
           createdAt: new Date(),
           updatedAt: new Date(),
         });
         await manager.save(user);
       } else {
-        if (isInvite) {
-          // user already invited to an organization, but not active - user tries to sign up
-          await manager.save(
-            Object.assign(existingUser, {
-              invitationToken: uuid.v4(),
-              defaultOrganizationId: organizationId,
-            })
-          );
-        }
         user = existingUser;
       }
+    });
+
+    await this.attachUserGroup(groups, organizationId, user.id);
+
+    return user;
+  }
 
+  async attachUserGroup(groups, organizationId, userId) {
+    await getManager().transaction(async (manager) => {
       for (const group of groups) {
         const orgGroupPermission = await manager.findOne(GroupPermission, {
           where: {
             organizationId: organizationId,
             group: group,
@@ -107,20 +103,18 @@ export class UsersService {
         });
 
         if (orgGroupPermission) {
           const userGroupPermission = manager.create(UserGroupPermission, {
             groupPermissionId: orgGroupPermission.id,
-            userId: user.id,
+            userId: userId,
           });
           await manager.save(userGroupPermission);
         } else {
           throw new BadRequestException(`${group} group does not exist for current organization`);
         }
       }
     });
-
-    return user;
   }
 
   async status(user: User) {
     const orgUser = await this.organizationUsersRepository.findOne({ where: { user } });
     return orgUser.status;
@@ -142,99 +136,10 @@ export class UsersService {
     newUserCreated = true;
 
     return { user, newUserCreated };
   }
 
-  async setupAccountFromInvitationToken(userCreateDto: CreateUserDto) {
-    const { organization, password, token, role, first_name: firstName, last_name: lastName } = userCreateDto;
-
-    if (!token) {
-      throw new BadRequestException('Invalid token');
-    }
-
-    const user: User = await this.usersRepository.findOne({ where: { invitationToken: token } });
-
-    if (!user?.organizationUsers) {
-      throw new BadRequestException('Invalid invitation link');
-    }
-    const organizationUser: OrganizationUser = user.organizationUsers.find(
-      (ou) => ou.organizationId === user.defaultOrganizationId
-    );
-
-    if (!organizationUser) {
-      throw new BadRequestException('Invalid invitation link');
-    }
-
-    await this.usersRepository.save(
-      Object.assign(user, {
-        firstName,
-        lastName,
-        password,
-        role,
-        invitationToken: null,
-      })
-    );
-
-    await this.organizationUsersRepository.save(
-      Object.assign(organizationUser, {
-        invitationToken: null,
-        status: 'active',
-      })
-    );
-
-    if (organization) {
-      await this.organizationsRepository.update(user.defaultOrganizationId, {
-        name: organization,
-      });
-    }
-  }
-
-  async acceptOrganizationInvite(params: any) {
-    const { password, token } = params;
-
-    const organizationUser = await this.organizationUsersRepository.findOne({
-      where: { invitationToken: token },
-      relations: ['user'],
-    });
-
-    if (!organizationUser?.user) {
-      throw new BadRequestException('Invalid invitation link');
-    }
-    const user: User = organizationUser.user;
-
-    if (user.invitationToken) {
-      // User sign up link send - not activated account
-      const defaultOrganizationUser = await this.organizationUsersRepository.findOne({
-        where: { organizationId: user.defaultOrganizationId, status: 'invited' },
-      });
-
-      if (defaultOrganizationUser) {
-        await this.organizationUsersRepository.save(
-          Object.assign(defaultOrganizationUser, {
-            invitationToken: null,
-            status: 'active',
-          })
-        );
-      }
-    }
-
-    // set new password if entered
-    await this.usersRepository.save(
-      Object.assign(user, {
-        ...(password ? { password } : {}),
-        invitationToken: null,
-      })
-    );
-
-    await this.organizationUsersRepository.save(
-      Object.assign(organizationUser, {
-        invitationToken: null,
-        status: 'active',
-      })
-    );
-  }
-
   async updateDefaultOrganization(user: User, organizationId: string) {
     await this.usersRepository.update(user.id, { defaultOrganizationId: organizationId });
   }
 
   async update(userId: string, params: any, manager?: EntityManager, organizationId?: string) {
diff --git a/server/test/controllers/app.e2e-spec.ts b/server/test/controllers/app.e2e-spec.ts
index a712fcb4..ce3252e8 100644
--- a/server/test/controllers/app.e2e-spec.ts
+++ b/server/test/controllers/app.e2e-spec.ts
@@ -1,15 +1,16 @@
 /* eslint-disable @typescript-eslint/no-unused-vars */
 import * as request from 'supertest';
 import { INestApplication } from '@nestjs/common';
-import { getManager, Repository } from 'typeorm';
+import { getManager, Repository, Not } from 'typeorm';
 import { User } from 'src/entities/user.entity';
 import { clearDB, createUser, authHeaderForUser, createNestAppInstanceWithEnvMock } from '../test.helper';
 import { OrganizationUser } from 'src/entities/organization_user.entity';
 import { Organization } from 'src/entities/organization.entity';
 import { SSOConfigs } from 'src/entities/sso_config.entity';
 import { EmailService } from '@services/email.service';
+import { v4 as uuidv4 } from 'uuid';
 
 describe('Authentication', () => {
   let app: INestApplication;
   let userRepository: Repository<User>;
   let orgRepository: Repository<Organization>;
@@ -170,11 +171,11 @@ describe('Authentication', () => {
           }
         });
       });
       it('should not create new users', async () => {
         const response = await request(app.getHttpServer()).post('/api/signup').send({ email: 'test@tooljet.io' });
-        expect(response.statusCode).toBe(406);
+        expect(response.statusCode).toBe(403);
       });
     });
     describe('sign up enabled and authorization', () => {
       it('should create new users', async () => {
         const response = await request(app.getHttpServer()).post('/api/signup').send({ email: 'test@tooljet.io' });
@@ -464,9 +465,396 @@ describe('Authentication', () => {
         .send({ email: 'admin@tooljet.io', password: 'new_password' })
         .expect(201);
     });
   });
 
+  describe('POST /api/set-password-from-token', () => {
+    beforeEach(() => {
+      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
+        switch (key) {
+          case 'DISABLE_MULTI_WORKSPACE':
+            return 'false';
+          default:
+            return process.env[key];
+        }
+      });
+    });
+    it('should allow users to setup account after sign up using  Multi-Workspace', async () => {
+      const invitationToken = uuidv4();
+      const userData = await createUser(app, {
+        email: 'signup@tooljet.io',
+        invitationToken,
+        status: 'invited',
+      });
+      const { user, organization } = userData;
+
+      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
+        first_name: 'signupuser',
+        last_name: 'user',
+        organization: 'org1',
+        password: uuidv4(),
+        token: invitationToken,
+        role: 'developer',
+      });
+
+      expect(response.statusCode).toBe(201);
+
+      const updatedUser = await getManager().findOneOrFail(User, { where: { email: user.email } });
+      expect(updatedUser.firstName).toEqual('signupuser');
+      expect(updatedUser.lastName).toEqual('user');
+      expect(updatedUser.defaultOrganizationId).toEqual(organization.id);
+      const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
+      expect(organizationUser.status).toEqual('active');
+    });
+
+    it('should return error if required params are not present - Multi-Workspace', async () => {
+      const invitationToken = uuidv4();
+      await createUser(app, {
+        email: 'signup@tooljet.io',
+        invitationToken,
+        status: 'invited',
+      });
+
+      const response = await request(app.getHttpServer()).post('/api/set-password-from-token');
+
+      expect(response.statusCode).toBe(400);
+      expect(response.body.message).toStrictEqual([
+        'password should not be empty',
+        'password must be a string',
+        'token should not be empty',
+        'token must be a string',
+      ]);
+    });
+
+    it('should allow users to setup account for single organization only once', async () => {
+      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
+        switch (key) {
+          case 'DISABLE_MULTI_WORKSPACE':
+            return 'true';
+          default:
+            return process.env[key];
+        }
+      });
+      const invitationToken = uuidv4();
+      await createUser(app, {
+        email: 'signup@tooljet.io',
+        invitationToken,
+        status: 'invited',
+      });
+
+      let response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
+        first_name: 'signupuser',
+        last_name: 'user',
+        organization: 'org1',
+        password: uuidv4(),
+        token: invitationToken,
+        role: 'developer',
+      });
+
+      expect(response.statusCode).toBe(201);
+
+      await createUser(app, {
+        email: 'signup2@tooljet.io',
+        invitationToken,
+        status: 'invited',
+      });
+
+      response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
+        first_name: 'signupuser2',
+        last_name: 'user2',
+        organization: 'org1',
+        password: uuidv4(),
+        token: invitationToken,
+        role: 'developer',
+      });
+
+      expect(response.statusCode).toBe(403);
+    });
+
+    it('should not allow users to setup account for Multi-Workspace and sign up disabled', async () => {
+      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
+        switch (key) {
+          case 'DISABLE_SIGNUPS':
+            return 'true';
+          default:
+            return process.env[key];
+        }
+      });
+      const invitationToken = uuidv4();
+      await createUser(app, {
+        email: 'signup@tooljet.io',
+        invitationToken,
+        status: 'invited',
+      });
+
+      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
+        first_name: 'signupuser',
+        last_name: 'user',
+        organization: 'org1',
+        password: uuidv4(),
+        token: invitationToken,
+        role: 'developer',
+      });
+
+      expect(response.statusCode).toBe(403);
+    });
+
+    it('should allow users to sign up and setup account if already invited to an organization but not setup the account', async () => {
+      const { organization: org, user: adminUser } = await createUser(app, {
+        email: 'admin@tooljet.io',
+      });
+
+      await request(app.getHttpServer())
+        .post(`/api/organization_users/`)
+        .set('Authorization', authHeaderForUser(adminUser))
+        .send({ email: 'invited@tooljet.io' })
+        .expect(201);
+
+      const signUpResponse = await request(app.getHttpServer())
+        .post('/api/signup')
+        .send({ email: 'invited@tooljet.io' });
+
+      expect(signUpResponse.statusCode).toBe(201);
+
+      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });
+
+      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);
+
+      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
+        first_name: 'signupuser',
+        last_name: 'user',
+        organization: 'org1',
+        password: uuidv4(),
+        token: invitedUserDetails.invitationToken,
+        role: 'developer',
+      });
+
+      expect(response.statusCode).toBe(201);
+      const updatedUser = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });
+      expect(updatedUser.firstName).toEqual('signupuser');
+      expect(updatedUser.lastName).toEqual('user');
+      expect(updatedUser.defaultOrganizationId).not.toBe(org.id);
+      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
+        where: { userId: Not(adminUser.id), organizationId: org.id },
+      });
+      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
+        where: { userId: Not(adminUser.id), organizationId: invitedUserDetails.defaultOrganizationId },
+      });
+      expect(organizationUser.status).toEqual('invited');
+      expect(defaultOrganizationUser.status).toEqual('active');
+
+      const acceptInviteResponse = await request(app.getHttpServer()).post('/api/accept-invite').send({
+        token: organizationUser.invitationToken,
+      });
+
+      expect(acceptInviteResponse.statusCode).toBe(201);
+
+      const organizationUserUpdated = await getManager().findOneOrFail(OrganizationUser, {
+        where: { userId: Not(adminUser.id), organizationId: org.id },
+      });
+      expect(organizationUserUpdated.status).toEqual('active');
+    });
+
+    it('should allow users setup account and accept invite', async () => {
+      const { organization: org, user: adminUser } = await createUser(app, {
+        email: 'admin@tooljet.io',
+      });
+
+      await request(app.getHttpServer())
+        .post(`/api/organization_users/`)
+        .set('Authorization', authHeaderForUser(adminUser))
+        .send({ email: 'invited@tooljet.io' })
+        .expect(201);
+
+      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });
+
+      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);
+
+      const organizationUserBeforeUpdate = await getManager().findOneOrFail(OrganizationUser, {
+        where: { userId: Not(adminUser.id), organizationId: org.id },
+      });
+
+      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
+        first_name: 'signupuser',
+        last_name: 'user',
+        organization: 'org1',
+        password: uuidv4(),
+        token: invitedUserDetails.invitationToken,
+        organizationToken: organizationUserBeforeUpdate.invitationToken,
+        role: 'developer',
+      });
+
+      expect(response.statusCode).toBe(201);
+      const updatedUser = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });
+      expect(updatedUser.firstName).toEqual('signupuser');
+      expect(updatedUser.lastName).toEqual('user');
+      expect(updatedUser.defaultOrganizationId).not.toBe(org.id);
+      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
+        where: { userId: Not(adminUser.id), organizationId: org.id },
+      });
+      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
+        where: { userId: Not(adminUser.id), organizationId: invitedUserDetails.defaultOrganizationId },
+      });
+      expect(organizationUser.status).toEqual('active');
+      expect(defaultOrganizationUser.status).toEqual('active');
+
+      const acceptInviteResponse = await request(app.getHttpServer()).post('/api/accept-invite').send({
+        token: organizationUser.invitationToken,
+      });
+
+      expect(acceptInviteResponse.statusCode).toBe(400);
+    });
+
+    it('should not allow users to setup account if already invited to an organization and trying to accept invite before setting up account', async () => {
+      const { organization: org, user: adminUser } = await createUser(app, {
+        email: 'admin@tooljet.io',
+      });
+
+      await request(app.getHttpServer())
+        .post(`/api/organization_users/`)
+        .set('Authorization', authHeaderForUser(adminUser))
+        .send({ email: 'invited@tooljet.io' })
+        .expect(201);
+
+      const signUpResponse = await request(app.getHttpServer())
+        .post('/api/signup')
+        .send({ email: 'invited@tooljet.io' });
+
+      expect(signUpResponse.statusCode).toBe(201);
+
+      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });
+      const orgUser = await getManager().findOneOrFail(OrganizationUser, {
+        where: { userId: invitedUserDetails.id, organizationId: org.id },
+      });
+
+      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);
+
+      const acceptInviteResponse = await request(app.getHttpServer()).post('/api/accept-invite').send({
+        token: orgUser.invitationToken,
+      });
+
+      expect(acceptInviteResponse.statusCode).toBe(401);
+
+      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
+        where: { userId: invitedUserDetails.id, organizationId: org.id },
+      });
+      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
+        where: { userId: invitedUserDetails.id, organizationId: invitedUserDetails.defaultOrganizationId },
+      });
+      expect(organizationUser.status).toEqual('invited');
+      expect(defaultOrganizationUser.status).toEqual('invited');
+
+      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
+        first_name: 'signupuser',
+        last_name: 'user',
+        organization: 'org1',
+        password: uuidv4(),
+        token: invitedUserDetails.invitationToken,
+        role: 'developer',
+      });
+
+      expect(response.statusCode).toBe(201);
+    });
+  });
+
+  describe('POST /api/accept-invite', () => {
+    describe('Multi-Worlspace Enabled', () => {
+      beforeEach(() => {
+        jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
+          switch (key) {
+            case 'DISABLE_MULTI_WORKSPACE':
+              return 'false';
+            default:
+              return process.env[key];
+          }
+        });
+      });
+
+      it('should allow users to accept invitation when Multi-Workspace is enabled', async () => {
+        const userData = await createUser(app, {
+          email: 'organizationUser@tooljet.io',
+          status: 'invited',
+        });
+
+        const { user, orgUser } = userData;
+
+        const response = await request(app.getHttpServer()).post('/api/accept-invite').send({
+          token: orgUser.invitationToken,
+        });
+
+        expect(response.statusCode).toBe(201);
+
+        const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
+        expect(organizationUser.status).toEqual('active');
+      });
+
+      it('should not allow users to accept invitation when user sign up is not completed', async () => {
+        const userData = await createUser(app, {
+          email: 'organizationUser@tooljet.io',
+          invitationToken: uuidv4(),
+          status: 'invited',
+        });
+        const { user, orgUser } = userData;
+
+        const response = await request(app.getHttpServer()).post('/api/accept-invite').send({
+          token: orgUser.invitationToken,
+        });
+
+        expect(response.statusCode).toBe(401);
+        expect(response.body.message).toBe(
+          'User not exist in the workspace, Please setup your account using link shared via email'
+        );
+      });
+    });
+
+    describe('Multi-Worlspace Disabled', () => {
+      beforeEach(() => {
+        jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
+          switch (key) {
+            case 'DISABLE_MULTI_WORKSPACE':
+              return 'true';
+            default:
+              return process.env[key];
+          }
+        });
+      });
+
+      it('should allow users to accept invitation when Multi-Workspace is disabled', async () => {
+        const userData = await createUser(app, {
+          email: 'organizationUser@tooljet.io',
+          status: 'invited',
+        });
+        const { user, orgUser } = userData;
+
+        const response = await request(app.getHttpServer()).post('/api/accept-invite').send({
+          token: orgUser.invitationToken,
+          password: uuidv4(),
+        });
+
+        expect(response.statusCode).toBe(201);
+
+        const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
+        expect(organizationUser.status).toEqual('active');
+      });
+
+      it('should not allow users to accept invitation when user not entered password for single workspace', async () => {
+        const userData = await createUser(app, {
+          email: 'organizationUser@tooljet.io',
+          invitationToken: uuidv4(),
+          status: 'invited',
+        });
+        const { orgUser } = userData;
+
+        const response = await request(app.getHttpServer()).post('/api/accept-invite').send({
+          token: orgUser.invitationToken,
+        });
+
+        expect(response.statusCode).toBe(400);
+        expect(response.body.message).toBe('Please enter password');
+      });
+    });
+  });
+
   afterAll(async () => {
     await app.close();
   });
 });
diff --git a/server/test/controllers/users.e2e-spec.ts b/server/test/controllers/users.e2e-spec.ts
index e97b3a66..447255e3 100644
--- a/server/test/controllers/users.e2e-spec.ts
+++ b/server/test/controllers/users.e2e-spec.ts
@@ -1,24 +1,21 @@
 import * as request from 'supertest';
 import { INestApplication } from '@nestjs/common';
-import { authHeaderForUser, clearDB, createUser, createNestAppInstanceWithEnvMock } from '../test.helper';
+import { authHeaderForUser, clearDB, createUser, createNestAppInstance } from '../test.helper';
 import { getManager } from 'typeorm';
 import { User } from 'src/entities/user.entity';
-import { v4 as uuidv4 } from 'uuid';
-import { OrganizationUser } from 'src/entities/organization_user.entity';
 const path = require('path');
 
 describe('users controller', () => {
   let app: INestApplication;
-  let mockConfig;
 
   beforeEach(async () => {
     await clearDB();
   });
 
   beforeAll(async () => {
-    ({ app, mockConfig } = await createNestAppInstanceWithEnvMock());
+    app = await createNestAppInstance();
   });
 
   afterEach(() => {
     jest.resetAllMocks();
     jest.clearAllMocks();
@@ -80,275 +77,10 @@ describe('users controller', () => {
       expect(updatedUser.firstName).toEqual(firstName);
       expect(updatedUser.lastName).toEqual(lastName);
     });
   });
 
-  describe('POST /api/users/set_password_from_token', () => {
-    it('should allow users to setup account after sign up using  Multi-Workspace', async () => {
-      const invitationToken = uuidv4();
-      const userData = await createUser(app, {
-        email: 'signup@tooljet.io',
-        invitationToken,
-        status: 'invited',
-      });
-      const { user, organization } = userData;
-
-      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
-        first_name: 'signupuser',
-        last_name: 'user',
-        organization: 'org1',
-        password: uuidv4(),
-        token: invitationToken,
-        role: 'developer',
-      });
-
-      expect(response.statusCode).toBe(201);
-
-      const updatedUser = await getManager().findOneOrFail(User, { where: { email: user.email } });
-      expect(updatedUser.firstName).toEqual('signupuser');
-      expect(updatedUser.lastName).toEqual('user');
-      expect(updatedUser.defaultOrganizationId).toEqual(organization.id);
-      const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
-      expect(organizationUser.status).toEqual('active');
-    });
-
-    it('should return error if required params are not present - Multi-Workspace', async () => {
-      const invitationToken = uuidv4();
-      await createUser(app, {
-        email: 'signup@tooljet.io',
-        invitationToken,
-        status: 'invited',
-      });
-
-      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token');
-
-      expect(response.statusCode).toBe(400);
-      expect(response.body.message).toStrictEqual([
-        'password should not be empty',
-        'password must be a string',
-        'token should not be empty',
-        'token must be a string',
-      ]);
-    });
-
-    it('should allow users to setup account for single organization only once', async () => {
-      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
-        switch (key) {
-          case 'DISABLE_MULTI_WORKSPACE':
-            return 'true';
-          default:
-            return process.env[key];
-        }
-      });
-      const invitationToken = uuidv4();
-      await createUser(app, {
-        email: 'signup@tooljet.io',
-        invitationToken,
-        status: 'invited',
-      });
-
-      let response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
-        first_name: 'signupuser',
-        last_name: 'user',
-        organization: 'org1',
-        password: uuidv4(),
-        token: invitationToken,
-        role: 'developer',
-      });
-
-      expect(response.statusCode).toBe(201);
-
-      await createUser(app, {
-        email: 'signup2@tooljet.io',
-        invitationToken,
-        status: 'invited',
-      });
-
-      response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
-        first_name: 'signupuser2',
-        last_name: 'user2',
-        organization: 'org1',
-        password: uuidv4(),
-        token: invitationToken,
-        role: 'developer',
-      });
-
-      expect(response.statusCode).toBe(403);
-    });
-
-    it('should not allow users to setup account for Multi-Workspace and sign up disabled', async () => {
-      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
-        switch (key) {
-          case 'DISABLE_SIGNUPS':
-            return 'true';
-          default:
-            return process.env[key];
-        }
-      });
-      const invitationToken = uuidv4();
-      await createUser(app, {
-        email: 'signup@tooljet.io',
-        invitationToken,
-        status: 'invited',
-      });
-
-      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
-        first_name: 'signupuser',
-        last_name: 'user',
-        organization: 'org1',
-        password: uuidv4(),
-        token: invitationToken,
-        role: 'developer',
-      });
-
-      expect(response.statusCode).toBe(403);
-    });
-
-    it('should allow users to setup account if already invited to an organization but not activated', async () => {
-      const org = (
-        await createUser(app, {
-          email: 'admin@tooljet.io',
-        })
-      ).organization;
-      const invitedUser = await createUser(app, {
-        email: 'invited@tooljet.io',
-        status: 'invited',
-        organization: org,
-      });
-
-      const signUpResponse = await request(app.getHttpServer())
-        .post('/api/signup')
-        .send({ email: 'invited@tooljet.io' });
-
-      expect(signUpResponse.statusCode).toBe(201);
-
-      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: invitedUser.user.email } });
-
-      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);
-
-      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
-        first_name: 'signupuser',
-        last_name: 'user',
-        organization: 'org1',
-        password: uuidv4(),
-        token: invitedUserDetails.invitationToken,
-        role: 'developer',
-      });
-
-      expect(response.statusCode).toBe(201);
-      const updatedUser = await getManager().findOneOrFail(User, { where: { email: invitedUser.user.email } });
-      expect(updatedUser.firstName).toEqual('signupuser');
-      expect(updatedUser.lastName).toEqual('user');
-      expect(updatedUser.defaultOrganizationId).not.toBe(org.id);
-      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
-        where: { userId: invitedUser.user.id, organizationId: org.id },
-      });
-      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
-        where: { userId: invitedUser.user.id, organizationId: invitedUserDetails.defaultOrganizationId },
-      });
-      expect(organizationUser.status).toEqual('invited');
-      expect(defaultOrganizationUser.status).toEqual('active');
-    });
-
-    it('should not allow users to setup account if already invited to an organization and activated account through invite link after sign up', async () => {
-      const { organization: org } = await createUser(app, {
-        email: 'admin@tooljet.io',
-      });
-      const invitedUser = await createUser(app, {
-        email: 'invited@tooljet.io',
-        status: 'invited',
-        organization: org,
-      });
-
-      const signUpResponse = await request(app.getHttpServer())
-        .post('/api/signup')
-        .send({ email: 'invited@tooljet.io' });
-
-      expect(signUpResponse.statusCode).toBe(201);
-
-      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: invitedUser.user.email } });
-
-      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);
-
-      const acceptInviteResponse = await request(app.getHttpServer()).post('/api/users/accept-invite').send({
-        token: invitedUser.orgUser.invitationToken,
-        password: 'new-password',
-      });
-
-      expect(acceptInviteResponse.statusCode).toBe(201);
-
-      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
-        where: { userId: invitedUser.user.id, organizationId: org.id },
-      });
-      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
-        where: { userId: invitedUser.user.id, organizationId: invitedUserDetails.defaultOrganizationId },
-      });
-      expect(organizationUser.status).toEqual('active');
-      expect(defaultOrganizationUser.status).toEqual('active');
-
-      const updatedUser = await getManager().findOneOrFail(User, { where: { email: invitedUser.user.email } });
-      expect(updatedUser.defaultOrganizationId).toBe(defaultOrganizationUser.organizationId);
-
-      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
-        first_name: 'signupuser',
-        last_name: 'user',
-        organization: 'org1',
-        password: uuidv4(),
-        token: invitedUserDetails.invitationToken,
-        role: 'developer',
-      });
-
-      expect(response.statusCode).toBe(400);
-    });
-  });
-
-  describe('POST /api/users/accept-invite', () => {
-    it('should allow users to accept invitation when Multi-Workspace is enabled', async () => {
-      const userData = await createUser(app, {
-        email: 'organizationUser@tooljet.io',
-        status: 'invited',
-      });
-      const { user, orgUser } = userData;
-
-      const response = await request(app.getHttpServer()).post('/api/users/accept-invite').send({
-        token: orgUser.invitationToken,
-        password: uuidv4(),
-      });
-
-      expect(response.statusCode).toBe(201);
-
-      const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
-      expect(organizationUser.status).toEqual('active');
-    });
-
-    it('should allow users to accept invitation when Multi-Workspace is disabled', async () => {
-      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
-        switch (key) {
-          case 'DISABLE_MULTI_WORKSPACE':
-            return 'true';
-          default:
-            return process.env[key];
-        }
-      });
-      const userData = await createUser(app, {
-        email: 'organizationUser@tooljet.io',
-        status: 'invited',
-      });
-      const { user, orgUser } = userData;
-
-      const response = await request(app.getHttpServer()).post('/api/users/accept-invite').send({
-        token: orgUser.invitationToken,
-        password: uuidv4(),
-      });
-
-      expect(response.statusCode).toBe(201);
-
-      const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
-      expect(organizationUser.status).toEqual('active');
-    });
-  });
-
   describe('POST /api/users/avatar', () => {
     it('should allow users to add a avatar', async () => {
       const userData = await createUser(app, { email: 'admin@tooljet.io' });
 
       const { user } = userData;

Vulnerable code: import { userService } from '@/_services';
    const token = this.props.location.state.token;
    userService
import { userService } from '@/_services';
    userService
      .then(() => {
        toast.success(`Added to the workspace${isSetPassword ? ' and password has been set ' : ' '}successfully.`, {
          position: 'top-center',
        this.props.history.push('/login');
      })
      .catch(({ error }) => {
        this.setState({ isLoading: false });
        toast.error(error, { position: 'top-center' });
              {!this.single_organization && (
                  <div className="org-invite-or">
                    <h2>
                      <span>OR</span>
                    </h2>
              <h2 className="card-title text-center mb-4">Set up your account</h2>
              <div className="mb-3">
                <label className="form-label">Password</label>
                <div className="input-group input-group-flat">
                  <input
                    onChange={this.handleChange}
                    name="password"
                    type="password"
                    className="form-control"
                    autoComplete="off"
                  />
                  <span className="input-group-text"></span>
                </div>
              </div>
              <div className="mb-3">
                <label className="form-label">Confirm Password</label>
                <div className="input-group input-group-flat">
                  <input
                    onChange={this.handleChange}
                    name="password_confirmation"
                    type="password"
                    className="form-control"
                    autoComplete="off"
                  />
                  <span className="input-group-text"></span>
                </div>
              </div>
              <div className="form-footer">
                <p>
                  By clicking the button below, you agree to our{' '}
                  <a href="https://tooljet.io/terms">Terms and Conditions</a>.
                </p>
                <button
                  className={`btn mt-2 btn-primary w-100 ${isLoading ? ' btn-loading' : ''}`}
                  onClick={(e) => this.acceptInvite(e, true)}
                  disabled={isLoading}
                >
                  Finish account setup and accept invite
                </button>
              </div>
import { history } from '@/_helpers';
    console.log(email);
  logout = () => {
    authenticationService.logout();
    history.push('/login');
  };

  setPasswordFromToken,
  acceptInvite,
function setPasswordFromToken({ token, password, organization, role, firstName, lastName }) {
  const body = {
    token,
    password,
    organization,
    role,
    first_name: firstName,
    last_name: lastName,
  };

  const requestOptions = { method: 'POST', headers: authHeader(), body: JSON.stringify(body) };
  return fetch(`${config.apiUrl}/users/set_password_from_token`, requestOptions).then(handleResponse);
}

function acceptInvite({ token, password }) {
  const body = {
    token,
    password,
  };

  const requestOptions = { method: 'POST', headers: authHeader(), body: JSON.stringify(body) };
  return fetch(`${config.apiUrl}/users/accept-invite`, requestOptions).then(handleResponse);
}

  constructor(private organizationUsersService: OrganizationUsersService) {}
    const result = await this.organizationUsersService.inviteNewUser(user, inviteNewUserDto);
import { Body, Controller, Post, Patch, UseGuards, UseInterceptors, Req, UploadedFile } from '@nestjs/common';
import { SignupDisableGuard } from 'src/modules/auth/signup-disable.guard';
import { CreateUserDto, UpdateUserDto } from '@dto/user.dto';
import { AcceptInviteDto } from '@dto/accept-organization-invite.dto';
import { MultiOrganizationGuard } from 'src/modules/auth/multi-organization.guard';
  @UseGuards(MultiOrganizationGuard, SignupDisableGuard)
  @Post('set_password_from_token')
  async create(@Body() userCreateDto: CreateUserDto) {
    await this.usersService.setupAccountFromInvitationToken(userCreateDto);
    return {};
  }

  @Post('accept-invite')
  async acceptInvite(@Body() acceptInviteDto: AcceptInviteDto) {
    await this.usersService.acceptOrganizationInvite(acceptInviteDto);
    return {};
  }

  async addAvatar(@Req() req, @UploadedFile() file: Express.Multer.File) {
    return this.usersService.addAvatar(req.user.id, file.buffer, file.originalname);
import { IsString, IsOptional, IsNotEmpty } from 'class-validator';
import { Injectable, NotAcceptableException, NotFoundException, UnauthorizedException } from '@nestjs/common';
  private async validateUser(email: string, password: string, organisationId?: string): Promise<User> {
    const user = await this.usersService.findByEmail(email, organisationId);
    if (existingUser?.invitationToken || existingUser?.organizationUsers?.some((ou) => ou.status === 'active')) {
  async sendWelcomeEmail(to: string, name: string, invitationtoken: string) {
    const inviteUrl = `${this.TOOLJET_HOST}/invitations/${invitationtoken}`;
    organisationName: string
          ${sender} has invited you to use ToolJet workspace ${organisationName}. Use the link below to set up your account and get started.
import { InviteNewUserDto } from '@dto/invite-new-user.dto';
  async inviteNewUser(currentUser: User, inviteNewUserDto: InviteNewUserDto): Promise<OrganizationUser> {
    const userParams = <User>{
      firstName: inviteNewUserDto.first_name,
      lastName: inviteNewUserDto.last_name,
      email: inviteNewUserDto.email,
    };

    let user = await this.usersService.findByEmail(userParams.email);

    if (user?.organizationUsers?.some((ou) => ou.organizationId === currentUser.organizationId)) {
      throw new BadRequestException('User with such email already exists.');
    }

    if (user?.invitationToken) {
      // user sign up not completed, name will be empty - updating name
      await this.usersService.update(user.id, { firstName: userParams.firstName, lastName: userParams.lastName });
    }

    user = await this.usersService.create(userParams, currentUser.organizationId, ['all_users'], user);

    const currentOrganization: Organization = (
      await this.organizationUsersRepository.findOne({
        where: { userId: currentUser.id, organizationId: currentUser.organizationId },
        relations: ['organization'],
      })
    )?.organization;

    const organizationUser: OrganizationUser = await this.create(user, currentOrganization, true);

    await this.emailService.sendOrganizationUserWelcomeEmail(
      user.email,
      user.firstName,
      currentUser.firstName,
      organizationUser.invitationToken,
      currentOrganization.name
    );

    return organizationUser;
  }

    private encryptionService: EncryptionService
import { Organization } from 'src/entities/organization.entity';
import { CreateUserDto } from '@dto/user.dto';
    @InjectRepository(Organization)
    private organizationsRepository: Repository<Organization>,
    isInvite?: boolean
          defaultOrganizationId: organizationId,
        if (isInvite) {
          // user already invited to an organization, but not active - user tries to sign up
          await manager.save(
            Object.assign(existingUser, {
              invitationToken: uuid.v4(),
              defaultOrganizationId: organizationId,
            })
          );
        }
            userId: user.id,

    return user;
  async setupAccountFromInvitationToken(userCreateDto: CreateUserDto) {
    const { organization, password, token, role, first_name: firstName, last_name: lastName } = userCreateDto;

    if (!token) {
      throw new BadRequestException('Invalid token');
    }

    const user: User = await this.usersRepository.findOne({ where: { invitationToken: token } });

    if (!user?.organizationUsers) {
      throw new BadRequestException('Invalid invitation link');
    }
    const organizationUser: OrganizationUser = user.organizationUsers.find(
      (ou) => ou.organizationId === user.defaultOrganizationId
    );

    if (!organizationUser) {
      throw new BadRequestException('Invalid invitation link');
    }

    await this.usersRepository.save(
      Object.assign(user, {
        firstName,
        lastName,
        password,
        role,
        invitationToken: null,
      })
    );

    await this.organizationUsersRepository.save(
      Object.assign(organizationUser, {
        invitationToken: null,
        status: 'active',
      })
    );

    if (organization) {
      await this.organizationsRepository.update(user.defaultOrganizationId, {
        name: organization,
      });
    }
  }

  async acceptOrganizationInvite(params: any) {
    const { password, token } = params;

    const organizationUser = await this.organizationUsersRepository.findOne({
      where: { invitationToken: token },
      relations: ['user'],
    });

    if (!organizationUser?.user) {
      throw new BadRequestException('Invalid invitation link');
    }
    const user: User = organizationUser.user;

    if (user.invitationToken) {
      // User sign up link send - not activated account
      const defaultOrganizationUser = await this.organizationUsersRepository.findOne({
        where: { organizationId: user.defaultOrganizationId, status: 'invited' },
      });

      if (defaultOrganizationUser) {
        await this.organizationUsersRepository.save(
          Object.assign(defaultOrganizationUser, {
            invitationToken: null,
            status: 'active',
          })
        );
      }
    }

    // set new password if entered
    await this.usersRepository.save(
      Object.assign(user, {
        ...(password ? { password } : {}),
        invitationToken: null,
      })
    );

    await this.organizationUsersRepository.save(
      Object.assign(organizationUser, {
        invitationToken: null,
        status: 'active',
      })
    );
  }

import { getManager, Repository } from 'typeorm';
        expect(response.statusCode).toBe(406);
import { authHeaderForUser, clearDB, createUser, createNestAppInstanceWithEnvMock } from '../test.helper';
import { v4 as uuidv4 } from 'uuid';
import { OrganizationUser } from 'src/entities/organization_user.entity';
  let mockConfig;
    ({ app, mockConfig } = await createNestAppInstanceWithEnvMock());
  describe('POST /api/users/set_password_from_token', () => {
    it('should allow users to setup account after sign up using  Multi-Workspace', async () => {
      const invitationToken = uuidv4();
      const userData = await createUser(app, {
        email: 'signup@tooljet.io',
        invitationToken,
        status: 'invited',
      });
      const { user, organization } = userData;

      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(201);

      const updatedUser = await getManager().findOneOrFail(User, { where: { email: user.email } });
      expect(updatedUser.firstName).toEqual('signupuser');
      expect(updatedUser.lastName).toEqual('user');
      expect(updatedUser.defaultOrganizationId).toEqual(organization.id);
      const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
      expect(organizationUser.status).toEqual('active');
    });

    it('should return error if required params are not present - Multi-Workspace', async () => {
      const invitationToken = uuidv4();
      await createUser(app, {
        email: 'signup@tooljet.io',
        invitationToken,
        status: 'invited',
      });

      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token');

      expect(response.statusCode).toBe(400);
      expect(response.body.message).toStrictEqual([
        'password should not be empty',
        'password must be a string',
        'token should not be empty',
        'token must be a string',
      ]);
    });

    it('should allow users to setup account for single organization only once', async () => {
      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
        switch (key) {
          case 'DISABLE_MULTI_WORKSPACE':
            return 'true';
          default:
            return process.env[key];
        }
      });
      const invitationToken = uuidv4();
      await createUser(app, {
        email: 'signup@tooljet.io',
        invitationToken,
        status: 'invited',
      });

      let response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(201);

      await createUser(app, {
        email: 'signup2@tooljet.io',
        invitationToken,
        status: 'invited',
      });

      response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
        first_name: 'signupuser2',
        last_name: 'user2',
        organization: 'org1',
        password: uuidv4(),
        token: invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(403);
    });

    it('should not allow users to setup account for Multi-Workspace and sign up disabled', async () => {
      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
        switch (key) {
          case 'DISABLE_SIGNUPS':
            return 'true';
          default:
            return process.env[key];
        }
      });
      const invitationToken = uuidv4();
      await createUser(app, {
        email: 'signup@tooljet.io',
        invitationToken,
        status: 'invited',
      });

      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(403);
    });

    it('should allow users to setup account if already invited to an organization but not activated', async () => {
      const org = (
        await createUser(app, {
          email: 'admin@tooljet.io',
        })
      ).organization;
      const invitedUser = await createUser(app, {
        email: 'invited@tooljet.io',
        status: 'invited',
        organization: org,
      });

      const signUpResponse = await request(app.getHttpServer())
        .post('/api/signup')
        .send({ email: 'invited@tooljet.io' });

      expect(signUpResponse.statusCode).toBe(201);

      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: invitedUser.user.email } });

      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);

      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitedUserDetails.invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(201);
      const updatedUser = await getManager().findOneOrFail(User, { where: { email: invitedUser.user.email } });
      expect(updatedUser.firstName).toEqual('signupuser');
      expect(updatedUser.lastName).toEqual('user');
      expect(updatedUser.defaultOrganizationId).not.toBe(org.id);
      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: invitedUser.user.id, organizationId: org.id },
      });
      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: invitedUser.user.id, organizationId: invitedUserDetails.defaultOrganizationId },
      });
      expect(organizationUser.status).toEqual('invited');
      expect(defaultOrganizationUser.status).toEqual('active');
    });

    it('should not allow users to setup account if already invited to an organization and activated account through invite link after sign up', async () => {
      const { organization: org } = await createUser(app, {
        email: 'admin@tooljet.io',
      });
      const invitedUser = await createUser(app, {
        email: 'invited@tooljet.io',
        status: 'invited',
        organization: org,
      });

      const signUpResponse = await request(app.getHttpServer())
        .post('/api/signup')
        .send({ email: 'invited@tooljet.io' });

      expect(signUpResponse.statusCode).toBe(201);

      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: invitedUser.user.email } });

      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);

      const acceptInviteResponse = await request(app.getHttpServer()).post('/api/users/accept-invite').send({
        token: invitedUser.orgUser.invitationToken,
        password: 'new-password',
      });

      expect(acceptInviteResponse.statusCode).toBe(201);

      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: invitedUser.user.id, organizationId: org.id },
      });
      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: invitedUser.user.id, organizationId: invitedUserDetails.defaultOrganizationId },
      });
      expect(organizationUser.status).toEqual('active');
      expect(defaultOrganizationUser.status).toEqual('active');

      const updatedUser = await getManager().findOneOrFail(User, { where: { email: invitedUser.user.email } });
      expect(updatedUser.defaultOrganizationId).toBe(defaultOrganizationUser.organizationId);

      const response = await request(app.getHttpServer()).post('/api/users/set_password_from_token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitedUserDetails.invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(400);
    });
  });

  describe('POST /api/users/accept-invite', () => {
    it('should allow users to accept invitation when Multi-Workspace is enabled', async () => {
      const userData = await createUser(app, {
        email: 'organizationUser@tooljet.io',
        status: 'invited',
      });
      const { user, orgUser } = userData;

      const response = await request(app.getHttpServer()).post('/api/users/accept-invite').send({
        token: orgUser.invitationToken,
        password: uuidv4(),
      });

      expect(response.statusCode).toBe(201);

      const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
      expect(organizationUser.status).toEqual('active');
    });

    it('should allow users to accept invitation when Multi-Workspace is disabled', async () => {
      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
        switch (key) {
          case 'DISABLE_MULTI_WORKSPACE':
            return 'true';
          default:
            return process.env[key];
        }
      });
      const userData = await createUser(app, {
        email: 'organizationUser@tooljet.io',
        status: 'invited',
      });
      const { user, orgUser } = userData;

      const response = await request(app.getHttpServer()).post('/api/users/accept-invite').send({
        token: orgUser.invitationToken,
        password: uuidv4(),
      });

      expect(response.statusCode).toBe(201);

      const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
      expect(organizationUser.status).toEqual('active');
    });
  });

Fixed code:             <Route
              path="/invitations/:token/workspaces/:organizationToken"
              render={(props) => (
                <Redirect
                  to={{
                    pathname: '/confirm',
                    state: {
                      token: props.match.params.token,
                      organizationToken: props.match.params.organizationToken,
                    },
                  }}
                />
              )}
            />
import { appService } from '@/_services';
    const { token, organizationToken } = this.props.location.state;
    appService
        organizationToken,
import { appService } from '@/_services';
    appService
      .then((response) => {
        response.json().then((data) => {
          if (!response.ok) {
            return toast.error(data?.message || 'Error while setting up your account.', { position: 'top-center' });
          }
          toast.success(`Added to the workspace${isSetPassword ? ' and password has been set ' : ' '}successfully.`, {
            position: 'top-center',
          });
          this.props.history.push('/login');
              {!this.single_organization ? (
                </>
              ) : (
                <>
                  <h2 className="card-title text-center mb-4">Set up your account</h2>
                  <div className="mb-3">
                    <label className="form-label">Password</label>
                    <div className="input-group input-group-flat">
                      <input
                        onChange={this.handleChange}
                        name="password"
                        type="password"
                        className="form-control"
                        autoComplete="off"
                      />
                      <span className="input-group-text"></span>
                    </div>
                  </div>
                  <div className="mb-3">
                    <label className="form-label">Confirm Password</label>
                    <div className="input-group input-group-flat">
                      <input
                        onChange={this.handleChange}
                        name="password_confirmation"
                        type="password"
                        className="form-control"
                        autoComplete="off"
                      />
                      <span className="input-group-text"></span>
                    </div>
                  </div>
                  <div className="form-footer">
                    <p>
                      By clicking the button below, you agree to our{' '}
                      <a href="https://tooljet.io/terms">Terms and Conditions</a>.
                    </p>
                    <button
                      className={`btn mt-2 btn-primary w-100 ${isLoading ? ' btn-loading' : ''}`}
                      onClick={(e) => this.acceptInvite(e, true)}
                      disabled={isLoading}
                    >
                      Finish account setup and accept invite
                    </button>
  setPasswordFromToken,
  acceptInvite,

function setPasswordFromToken({ token, password, organization, role, firstName, lastName, organizationToken }) {
  const body = {
    token,
    organizationToken,
    password,
    organization,
    role,
    first_name: firstName,
    last_name: lastName,
  };

  const requestOptions = { method: 'POST', headers: authHeader(), body: JSON.stringify(body) };
  return fetch(`${config.apiUrl}/set-password-from-token`, requestOptions).then(handleResponse);
}

function acceptInvite({ token, password }) {
  const body = {
    token,
    password,
  };

  const requestOptions = { method: 'POST', headers: authHeader(), body: JSON.stringify(body) };
  return fetch(`${config.apiUrl}/accept-invite`, requestOptions);
}
import { MultiOrganizationGuard } from 'src/modules/auth/multi-organization.guard';
import { SignupDisableGuard } from 'src/modules/auth/signup-disable.guard';
import { CreateUserDto } from '@dto/user.dto';
import { AcceptInviteDto } from '@dto/accept-organization-invite.dto';
  @UseGuards(MultiOrganizationGuard, SignupDisableGuard)
  @Post('set-password-from-token')
  async create(@Body() userCreateDto: CreateUserDto) {
    await this.authService.setupAccountFromInvitationToken(userCreateDto);
    return {};
  }

  @Post('accept-invite')
  async acceptInvite(@Body() acceptInviteDto: AcceptInviteDto) {
    await this.authService.acceptOrganizationInvite(acceptInviteDto);
    return {};
  }

  @UseGuards(MultiOrganizationGuard, SignupDisableGuard)
import { OrganizationsService } from '@services/organizations.service';
  constructor(
    private organizationUsersService: OrganizationUsersService,
    private organizationsService: OrganizationsService
  ) {}
    const result = await this.organizationsService.inviteNewUser(user, inviteNewUserDto);
import { Body, Controller, Post, Patch, UseGuards, UseInterceptors, UploadedFile } from '@nestjs/common';
import { UpdateUserDto } from '@dto/user.dto';
  async addAvatar(@User() user, @UploadedFile() file: Express.Multer.File) {
    return this.usersService.addAvatar(user.id, file.buffer, file.originalname);
import { IsString, IsNotEmpty, IsOptional } from 'class-validator';
  @IsString()
  @IsOptional()
  @IsNotEmpty()
  organizationToken: string;

import {
  BadRequestException,
  Injectable,
  NotAcceptableException,
  NotFoundException,
  UnauthorizedException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { OrganizationUser } from 'src/entities/organization_user.entity';
import { CreateUserDto } from '@dto/user.dto';
import { AcceptInviteDto } from '@dto/accept-organization-invite.dto';
    @InjectRepository(User)
    private usersRepository: Repository<User>,
    @InjectRepository(OrganizationUser)
    private organizationUsersRepository: Repository<OrganizationUser>,
    @InjectRepository(Organization)
    private organizationsRepository: Repository<Organization>,
  private async validateUser(email: string, password: string, organizationId?: string): Promise<User> {
    const user = await this.usersService.findByEmail(email, organizationId);
    if (existingUser?.organizationUsers?.some((ou) => ou.status === 'active')) {
    if (existingUser?.invitationToken) {
      await this.emailService.sendWelcomeEmail(
        existingUser.email,
        existingUser.firstName,
        existingUser.invitationToken
      );
      return;
    }


  async setupAccountFromInvitationToken(userCreateDto: CreateUserDto) {
    const {
      organization,
      password,
      token,
      role,
      first_name: firstName,
      last_name: lastName,
      organizationToken,
    } = userCreateDto;

    if (!token) {
      throw new BadRequestException('Invalid token');
    }

    const user: User = await this.usersRepository.findOne({ where: { invitationToken: token } });

    if (!user?.organizationUsers) {
      throw new BadRequestException('Invalid invitation link');
    }
    const organizationUser: OrganizationUser = user.organizationUsers.find(
      (ou) => ou.organizationId === user.defaultOrganizationId
    );

    if (!organizationUser) {
      throw new BadRequestException('Invalid invitation link');
    }

    await this.usersRepository.save(
      Object.assign(user, {
        firstName,
        lastName,
        password,
        role,
        invitationToken: null,
      })
    );

    await this.organizationUsersRepository.save(
      Object.assign(organizationUser, {
        invitationToken: null,
        status: 'active',
      })
    );

    if (organization) {
      await this.organizationsRepository.update(user.defaultOrganizationId, {
        name: organization,
      });
    }

    if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') !== 'true' && organizationToken) {
      const organizationUser = await this.organizationUsersRepository.findOne({
        where: { invitationToken: organizationToken },
      });

      if (organizationUser) {
        await this.organizationUsersRepository.save(
          Object.assign(organizationUser, {
            invitationToken: null,
            status: 'active',
          })
        );
      }
    }
  }

  async acceptOrganizationInvite(acceptInviteDto: AcceptInviteDto) {
    const { password, token } = acceptInviteDto;

    if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') === 'true' && !password) {
      throw new BadRequestException('Please enter password');
    }
    const organizationUser = await this.organizationUsersRepository.findOne({
      where: { invitationToken: token },
      relations: ['user'],
    });

    if (!organizationUser?.user) {
      throw new BadRequestException('Invalid invitation link');
    }
    const user: User = organizationUser.user;

    if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') !== 'true' && user.invitationToken) {
      // User sign up link send - not activated account
      this.emailService
        .sendWelcomeEmail(
          user.email,
          `${user.firstName} ${user.lastName}`,
          user.invitationToken,
          organizationUser.invitationToken
        )
        .catch((err) => console.error('Error while sending welcome mail', err));
      throw new UnauthorizedException(
        'User not exist in the workspace, Please setup your account using link shared via email'
      );
    }

    if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') === 'true') {
      // set new password
      await this.usersRepository.save(
        Object.assign(user, {
          ...(password ? { password } : {}),
          invitationToken: null,
        })
      );
    }

    await this.organizationUsersRepository.save(
      Object.assign(organizationUser, {
        invitationToken: null,
        status: 'active',
      })
    );
  }
  async sendWelcomeEmail(
    to: string,
    name: string,
    invitationtoken: string,
    organizationInvitationToken?: string,
    organizationName?: string,
    sender?: string
  ) {
    const inviteUrl = `${this.TOOLJET_HOST}/invitations/${invitationtoken}${
      organizationInvitationToken ? `/workspaces/${organizationInvitationToken}` : ''
    }`;
          ${
            organizationInvitationToken && sender && organizationName
              ? `<span>
              ${sender} has invited you to use ToolJet workspace: ${organizationName}.
            </span>`
              : ''
          }
    organizationName: string
          ${sender} has invited you to use ToolJet workspace: ${organizationName}. Use the link below to set up your account and get started.
import { EmailService } from './email.service';
import { InviteNewUserDto } from '@dto/invite-new-user.dto';
import { ConfigService } from '@nestjs/config';
    private encryptionService: EncryptionService,
    private emailService: EmailService,
    private configService: ConfigService

  async inviteNewUser(currentUser: User, inviteNewUserDto: InviteNewUserDto): Promise<OrganizationUser> {
    const userParams = <User>{
      firstName: inviteNewUserDto.first_name,
      lastName: inviteNewUserDto.last_name,
      email: inviteNewUserDto.email,
    };

    let user = await this.usersService.findByEmail(userParams.email);
    let defaultOrganisation: Organization,
      shouldSendWelcomeMail = false;

    if (user?.organizationUsers?.some((ou) => ou.organizationId === currentUser.organizationId)) {
      throw new BadRequestException('User with such email already exists.');
    }

    if (user?.invitationToken) {
      // user sign up not completed, name will be empty - updating name
      await this.usersService.update(user.id, { firstName: userParams.firstName, lastName: userParams.lastName });
    }

    if (!user && this.configService.get<string>('DISABLE_MULTI_WORKSPACE') !== 'true') {
      // User not exist
      shouldSendWelcomeMail = true;
      // Create default organization
      defaultOrganisation = await this.create('Untitled workspace');
    }
    user = await this.usersService.create(
      userParams,
      currentUser.organizationId,
      ['all_users'],
      user,
      true,
      defaultOrganisation?.id
    );

    if (defaultOrganisation) {
      // Setting up default organization
      await this.organizationUserService.create(user, defaultOrganisation, true);
      await this.usersService.attachUserGroup(['all_users', 'admin'], defaultOrganisation.id, user.id);
    }

    const currentOrganization: Organization = (
      await this.organizationUsersRepository.findOne({
        where: { userId: currentUser.id, organizationId: currentUser.organizationId },
        relations: ['organization'],
      })
    )?.organization;

    const organizationUser: OrganizationUser = await this.organizationUserService.create(
      user,
      currentOrganization,
      true
    );

    if (shouldSendWelcomeMail) {
      this.emailService
        .sendWelcomeEmail(
          user.email,
          user.firstName,
          user.invitationToken,
          organizationUser.invitationToken,
          currentOrganization.name,
          `${currentUser.firstName} ${currentUser.lastName}`
        )
        .catch((err) => console.error('Error while sending welcome mail', err));
    } else {
      this.emailService
        .sendOrganizationUserWelcomeEmail(
          user.email,
          user.firstName,
          `${currentUser.firstName} ${currentUser.lastName}`,
          organizationUser.invitationToken,
          currentOrganization.name
        )
        .catch((err) => console.error('Error while sending welcome mail', err));
    }
    return organizationUser;
  }
    isInvite?: boolean,
    defaultOrganizationId?: string
          defaultOrganizationId: defaultOrganizationId || organizationId,
    });

    await this.attachUserGroup(groups, organizationId, user.id);

    return user;
  }
  async attachUserGroup(groups, organizationId, userId) {
    await getManager().transaction(async (manager) => {
            userId: userId,
import { getManager, Repository, Not } from 'typeorm';
import { v4 as uuidv4 } from 'uuid';
        expect(response.statusCode).toBe(403);
  describe('POST /api/set-password-from-token', () => {
    beforeEach(() => {
      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
        switch (key) {
          case 'DISABLE_MULTI_WORKSPACE':
            return 'false';
          default:
            return process.env[key];
        }
      });
    });
    it('should allow users to setup account after sign up using  Multi-Workspace', async () => {
      const invitationToken = uuidv4();
      const userData = await createUser(app, {
        email: 'signup@tooljet.io',
        invitationToken,
        status: 'invited',
      });
      const { user, organization } = userData;

      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(201);

      const updatedUser = await getManager().findOneOrFail(User, { where: { email: user.email } });
      expect(updatedUser.firstName).toEqual('signupuser');
      expect(updatedUser.lastName).toEqual('user');
      expect(updatedUser.defaultOrganizationId).toEqual(organization.id);
      const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
      expect(organizationUser.status).toEqual('active');
    });

    it('should return error if required params are not present - Multi-Workspace', async () => {
      const invitationToken = uuidv4();
      await createUser(app, {
        email: 'signup@tooljet.io',
        invitationToken,
        status: 'invited',
      });

      const response = await request(app.getHttpServer()).post('/api/set-password-from-token');

      expect(response.statusCode).toBe(400);
      expect(response.body.message).toStrictEqual([
        'password should not be empty',
        'password must be a string',
        'token should not be empty',
        'token must be a string',
      ]);
    });

    it('should allow users to setup account for single organization only once', async () => {
      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
        switch (key) {
          case 'DISABLE_MULTI_WORKSPACE':
            return 'true';
          default:
            return process.env[key];
        }
      });
      const invitationToken = uuidv4();
      await createUser(app, {
        email: 'signup@tooljet.io',
        invitationToken,
        status: 'invited',
      });

      let response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(201);

      await createUser(app, {
        email: 'signup2@tooljet.io',
        invitationToken,
        status: 'invited',
      });

      response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
        first_name: 'signupuser2',
        last_name: 'user2',
        organization: 'org1',
        password: uuidv4(),
        token: invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(403);
    });

    it('should not allow users to setup account for Multi-Workspace and sign up disabled', async () => {
      jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
        switch (key) {
          case 'DISABLE_SIGNUPS':
            return 'true';
          default:
            return process.env[key];
        }
      });
      const invitationToken = uuidv4();
      await createUser(app, {
        email: 'signup@tooljet.io',
        invitationToken,
        status: 'invited',
      });

      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(403);
    });

    it('should allow users to sign up and setup account if already invited to an organization but not setup the account', async () => {
      const { organization: org, user: adminUser } = await createUser(app, {
        email: 'admin@tooljet.io',
      });

      await request(app.getHttpServer())
        .post(`/api/organization_users/`)
        .set('Authorization', authHeaderForUser(adminUser))
        .send({ email: 'invited@tooljet.io' })
        .expect(201);

      const signUpResponse = await request(app.getHttpServer())
        .post('/api/signup')
        .send({ email: 'invited@tooljet.io' });

      expect(signUpResponse.statusCode).toBe(201);

      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });

      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);

      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitedUserDetails.invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(201);
      const updatedUser = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });
      expect(updatedUser.firstName).toEqual('signupuser');
      expect(updatedUser.lastName).toEqual('user');
      expect(updatedUser.defaultOrganizationId).not.toBe(org.id);
      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: Not(adminUser.id), organizationId: org.id },
      });
      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: Not(adminUser.id), organizationId: invitedUserDetails.defaultOrganizationId },
      });
      expect(organizationUser.status).toEqual('invited');
      expect(defaultOrganizationUser.status).toEqual('active');

      const acceptInviteResponse = await request(app.getHttpServer()).post('/api/accept-invite').send({
        token: organizationUser.invitationToken,
      });

      expect(acceptInviteResponse.statusCode).toBe(201);

      const organizationUserUpdated = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: Not(adminUser.id), organizationId: org.id },
      });
      expect(organizationUserUpdated.status).toEqual('active');
    });

    it('should allow users setup account and accept invite', async () => {
      const { organization: org, user: adminUser } = await createUser(app, {
        email: 'admin@tooljet.io',
      });

      await request(app.getHttpServer())
        .post(`/api/organization_users/`)
        .set('Authorization', authHeaderForUser(adminUser))
        .send({ email: 'invited@tooljet.io' })
        .expect(201);

      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });

      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);

      const organizationUserBeforeUpdate = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: Not(adminUser.id), organizationId: org.id },
      });

      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitedUserDetails.invitationToken,
        organizationToken: organizationUserBeforeUpdate.invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(201);
      const updatedUser = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });
      expect(updatedUser.firstName).toEqual('signupuser');
      expect(updatedUser.lastName).toEqual('user');
      expect(updatedUser.defaultOrganizationId).not.toBe(org.id);
      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: Not(adminUser.id), organizationId: org.id },
      });
      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: Not(adminUser.id), organizationId: invitedUserDetails.defaultOrganizationId },
      });
      expect(organizationUser.status).toEqual('active');
      expect(defaultOrganizationUser.status).toEqual('active');

      const acceptInviteResponse = await request(app.getHttpServer()).post('/api/accept-invite').send({
        token: organizationUser.invitationToken,
      });

      expect(acceptInviteResponse.statusCode).toBe(400);
    });

    it('should not allow users to setup account if already invited to an organization and trying to accept invite before setting up account', async () => {
      const { organization: org, user: adminUser } = await createUser(app, {
        email: 'admin@tooljet.io',
      });

      await request(app.getHttpServer())
        .post(`/api/organization_users/`)
        .set('Authorization', authHeaderForUser(adminUser))
        .send({ email: 'invited@tooljet.io' })
        .expect(201);

      const signUpResponse = await request(app.getHttpServer())
        .post('/api/signup')
        .send({ email: 'invited@tooljet.io' });

      expect(signUpResponse.statusCode).toBe(201);

      const invitedUserDetails = await getManager().findOneOrFail(User, { where: { email: 'invited@tooljet.io' } });
      const orgUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: invitedUserDetails.id, organizationId: org.id },
      });

      expect(invitedUserDetails.defaultOrganizationId).not.toBe(org.id);

      const acceptInviteResponse = await request(app.getHttpServer()).post('/api/accept-invite').send({
        token: orgUser.invitationToken,
      });

      expect(acceptInviteResponse.statusCode).toBe(401);

      const organizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: invitedUserDetails.id, organizationId: org.id },
      });
      const defaultOrganizationUser = await getManager().findOneOrFail(OrganizationUser, {
        where: { userId: invitedUserDetails.id, organizationId: invitedUserDetails.defaultOrganizationId },
      });
      expect(organizationUser.status).toEqual('invited');
      expect(defaultOrganizationUser.status).toEqual('invited');

      const response = await request(app.getHttpServer()).post('/api/set-password-from-token').send({
        first_name: 'signupuser',
        last_name: 'user',
        organization: 'org1',
        password: uuidv4(),
        token: invitedUserDetails.invitationToken,
        role: 'developer',
      });

      expect(response.statusCode).toBe(201);
    });
  });

  describe('POST /api/accept-invite', () => {
    describe('Multi-Worlspace Enabled', () => {
      beforeEach(() => {
        jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
          switch (key) {
            case 'DISABLE_MULTI_WORKSPACE':
              return 'false';
            default:
              return process.env[key];
          }
        });
      });

      it('should allow users to accept invitation when Multi-Workspace is enabled', async () => {
        const userData = await createUser(app, {
          email: 'organizationUser@tooljet.io',
          status: 'invited',
        });

        const { user, orgUser } = userData;

        const response = await request(app.getHttpServer()).post('/api/accept-invite').send({
          token: orgUser.invitationToken,
        });

        expect(response.statusCode).toBe(201);

        const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
        expect(organizationUser.status).toEqual('active');
      });

      it('should not allow users to accept invitation when user sign up is not completed', async () => {
        const userData = await createUser(app, {
          email: 'organizationUser@tooljet.io',
          invitationToken: uuidv4(),
          status: 'invited',
        });
        const { user, orgUser } = userData;

        const response = await request(app.getHttpServer()).post('/api/accept-invite').send({
          token: orgUser.invitationToken,
        });

        expect(response.statusCode).toBe(401);
        expect(response.body.message).toBe(
          'User not exist in the workspace, Please setup your account using link shared via email'
        );
      });
    });

    describe('Multi-Worlspace Disabled', () => {
      beforeEach(() => {
        jest.spyOn(mockConfig, 'get').mockImplementation((key: string) => {
          switch (key) {
            case 'DISABLE_MULTI_WORKSPACE':
              return 'true';
            default:
              return process.env[key];
          }
        });
      });

      it('should allow users to accept invitation when Multi-Workspace is disabled', async () => {
        const userData = await createUser(app, {
          email: 'organizationUser@tooljet.io',
          status: 'invited',
        });
        const { user, orgUser } = userData;

        const response = await request(app.getHttpServer()).post('/api/accept-invite').send({
          token: orgUser.invitationToken,
          password: uuidv4(),
        });

        expect(response.statusCode).toBe(201);

        const organizationUser = await getManager().findOneOrFail(OrganizationUser, { where: { userId: user.id } });
        expect(organizationUser.status).toEqual('active');
      });

      it('should not allow users to accept invitation when user not entered password for single workspace', async () => {
        const userData = await createUser(app, {
          email: 'organizationUser@tooljet.io',
          invitationToken: uuidv4(),
          status: 'invited',
        });
        const { orgUser } = userData;

        const response = await request(app.getHttpServer()).post('/api/accept-invite').send({
          token: orgUser.invitationToken,
        });

        expect(response.statusCode).toBe(400);
        expect(response.body.message).toBe('Please enter password');
      });
    });
  });

import { authHeaderForUser, clearDB, createUser, createNestAppInstance } from '../test.helper';
    app = await createNestAppInstance();