ToolJet/server/src/services/auth.service.ts

218 lines
8.3 KiB
TypeScript
Raw Normal View History

import { Injectable, NotAcceptableException, NotFoundException, UnauthorizedException } from '@nestjs/common';
import { UsersService } from './users.service';
2021-07-19 05:42:16 +00:00
import { OrganizationsService } from './organizations.service';
2021-07-08 07:39:07 +00:00
import { JwtService } from '@nestjs/jwt';
2021-07-11 05:13:51 +00:00
import { User } from '../entities/user.entity';
2021-07-19 05:42:16 +00:00
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';
2021-07-08 07:39:07 +00:00
const bcrypt = require('bcrypt');
const uuid = require('uuid');
2021-07-08 07:39:07 +00:00
@Injectable()
export class AuthService {
constructor(
private usersService: UsersService,
2021-07-19 05:42:16 +00:00
private jwtService: JwtService,
private organizationsService: OrganizationsService,
private organizationUsersService: OrganizationUsersService,
private emailService: EmailService,
private configService: ConfigService
) {}
2021-07-08 07:39:07 +00:00
verifyToken(token: string) {
try {
const signedJwt = this.jwtService.verify(token);
return signedJwt;
} catch (err) {
return null;
}
}
private async validateUser(email: string, password: string, organisationId?: string): Promise<User> {
const user = await this.usersService.findByEmail(email, organisationId);
if (!user) return null;
const isVerified = await bcrypt.compare(password, user.password);
2021-07-08 07:39:07 +00:00
return isVerified ? user : null;
}
async login(email: string, password: string, organizationId?: string) {
let organization: Organization;
const user = await this.validateUser(email, password, organizationId);
2021-07-08 07:39:07 +00:00
if (user && (await this.usersService.status(user)) !== 'archived') {
if (!organizationId) {
// Global login
// Determine the organization to be loaded
if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') === 'true') {
// Single organization
organization = await this.organizationsService.getSingleOrganization();
if (!organization?.ssoConfigs?.find((oc) => oc.sso == 'form' && oc.enabled)) {
throw new UnauthorizedException();
}
} else {
const organizationList: Organization[] = await this.organizationsService.findOrganizationSupportsFormLogin(
user
);
const defaultOrgDetails: Organization = organizationList?.find((og) => og.id === user.defaultOrganizationId);
// Multi organization
if (defaultOrgDetails) {
// default organization form login enabled
organization = defaultOrgDetails;
} else if (organizationList?.length > 0) {
// default organization form login not enabled, picking first one from form enabled list
organization = organizationList[0];
} else {
// no form login enabled organization available for user - creating new one
organization = await this.organizationsService.create('Untitled workspace', user);
}
}
user.organizationId = organization.id;
} else {
// organization specific login
user.organizationId = organizationId;
organization = await this.organizationsService.get(user.organizationId);
const formConfigs: SSOConfigs = organization?.ssoConfigs?.find((sso) => sso.sso === 'form');
if (!formConfigs?.enabled) {
// no configurations in organization side or Form login disabled for the organization
throw new UnauthorizedException('Password login is disabled for the organization');
}
}
if (user.defaultOrganizationId !== user.organizationId) {
// Updating default organization Id
await this.usersService.updateDefaultOrganization(user, organization.id);
}
const payload = {
username: user.id,
sub: user.email,
organizationId: user.organizationId,
isPasswordLogin: true,
};
2021-07-08 07:39:07 +00:00
return decamelizeKeys({
id: user.id,
2021-07-10 13:54:32 +00:00
auth_token: this.jwtService.sign(payload),
email: user.email,
first_name: user.firstName,
last_name: user.lastName,
organizationId: user.organizationId,
organization: organization.name,
Feature: User access management 🔥 (#918) * create migrations for group permissions setup * define new entities and relationships * revise migrations * rename columns * add migration to populate permission groups for existing users * Feature: User access permission group usage (#883) * create migrations for group permissions setup * define new entities and relationships * revise migrations * rename columns * add migration to populate permission groups for existing users * revise migrations * hide roles usage * setup group permissions for apps and users * fix defaultChecked * fix update permission checkbox * fix casl ability check to have params passed * fix casl apps abilities to check with app specific permission * add ability to delete groups * conditionally render edit and delete options for all and admin users * fix user role to group migration * revise group management pages to disallow updating default group * move manage users and groups to navbar dropdown * show only addable apps and users on dropdowns * rename header as profile settings * scope addable apps and users by organization * scope viewable apps on homepage * hide manage groups link from non admins * make permissions to be used with radio input * add loading state for add apps/users buttons * revise unit tests * revise migrations * fix e2e tests * comment out dead code * fix seeds script * handle folder count * captalize error toast * hide manage users dropdown for non admins * show fobidden error on blank homepage * fix folder app count * fix invalid state set * make group name clickable for edit instead * users with edit permission can deploy apps * not show edit link on homepage if user dont have update permission * remove unused entity from merge * remove roles usage from manage org users page * fix folder count and blank slate on homepage * disable add buttons if there is no selections * humanize default groups on view * make app added onto groups have read permission by default * not show app menu if user is not admin * remove admin users from group user addition dropdown * create default permissions for app cloned * fix querying index page without page params * fix admin scoped out from group add * remove apps from header * fix invitation url not shown * scope admin deletion check by org * scope public apps by organization * add specs for group permissions e2e * removed unused entity and add group permissions spec * remove console logs * remove unused permission * scope public app count by org * remove console log * refactor manage group permission resources component * update group permssion in org scope
2021-10-11 15:15:58 +00:00
admin: await this.usersService.hasGroup(user, 'admin'),
group_permissions: await this.usersService.groupPermissions(user),
app_group_permissions: await this.usersService.appGroupPermissions(user),
});
2021-07-08 07:39:07 +00:00
} else {
throw new UnauthorizedException('Invalid credentials');
}
}
2021-07-19 05:42:16 +00:00
async switchOrganization(newOrganizationId: string, user: User, isNewOrganization?: boolean) {
if (!(isNewOrganization || user.isPasswordLogin)) {
throw new UnauthorizedException();
}
if (this.configService.get<string>('DISABLE_MULTI_WORKSPACE') === 'true') {
throw new UnauthorizedException();
2021-07-19 05:42:16 +00:00
}
const newUser = await this.usersService.findByEmail(user.email, newOrganizationId);
if (newUser && (await this.usersService.status(newUser)) !== 'archived') {
newUser.organizationId = newOrganizationId;
const organization: Organization = await this.organizationsService.get(newUser.organizationId);
const formConfigs: SSOConfigs = organization?.ssoConfigs?.find((sso) => sso.sso === 'form');
if (!formConfigs?.enabled) {
// no configurations in organization side or Form login disabled for the organization
throw new UnauthorizedException('Password login disabled for the organization');
}
// Updating default organization Id
await this.usersService.updateDefaultOrganization(newUser, newUser.organizationId);
const payload = {
username: user.id,
sub: user.email,
organizationId: newUser.organizationId,
isPasswordLogin: true,
};
return decamelizeKeys({
id: newUser.id,
auth_token: this.jwtService.sign(payload),
email: newUser.email,
first_name: newUser.firstName,
last_name: newUser.lastName,
organizationId: newUser.organizationId,
organization: organization.name,
admin: await this.usersService.hasGroup(newUser, 'admin'),
group_permissions: await this.usersService.groupPermissions(newUser),
app_group_permissions: await this.usersService.appGroupPermissions(newUser),
});
} else {
throw new UnauthorizedException('Invalid credentials');
}
}
async signup(email: string) {
const existingUser = await this.usersService.findByEmail(email);
if (existingUser?.invitationToken || existingUser?.organizationUsers?.some((ou) => ou.status === 'active')) {
throw new NotAcceptableException('Email already exists');
}
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();
2021-07-19 05:42:16 +00:00
if (organization) {
throw new NotAcceptableException('Multi organization not supported - organization exist');
}
} else {
// Multi organization
if (this.configService.get<string>('DISABLE_SIGNUPS') === 'true') {
throw new NotAcceptableException();
}
}
// Create default organization
organization = await this.organizationsService.create('Untitled workspace');
const user = await this.usersService.create({ email }, organization.id, ['all_users', 'admin'], existingUser, true);
await this.organizationUsersService.create(user, organization, true);
await this.emailService.sendWelcomeEmail(user.email, user.firstName, user.invitationToken);
2022-01-19 09:32:36 +00:00
return {};
2021-07-26 16:02:47 +00:00
}
async forgotPassword(email: string) {
const user = await this.usersService.findByEmail(email);
const forgotPasswordToken = uuid.v4();
await this.usersService.update(user.id, { forgotPasswordToken });
await this.emailService.sendPasswordResetEmail(email, forgotPasswordToken);
2021-07-26 16:02:47 +00:00
}
async resetPassword(token: string, password: string) {
const user = await this.usersService.findByPasswordResetToken(token);
if (!user) {
throw new NotFoundException('Invalid token');
2021-07-26 16:02:47 +00:00
} else {
await this.usersService.update(user.id, {
Feature: User access management 🔥 (#918) * create migrations for group permissions setup * define new entities and relationships * revise migrations * rename columns * add migration to populate permission groups for existing users * Feature: User access permission group usage (#883) * create migrations for group permissions setup * define new entities and relationships * revise migrations * rename columns * add migration to populate permission groups for existing users * revise migrations * hide roles usage * setup group permissions for apps and users * fix defaultChecked * fix update permission checkbox * fix casl ability check to have params passed * fix casl apps abilities to check with app specific permission * add ability to delete groups * conditionally render edit and delete options for all and admin users * fix user role to group migration * revise group management pages to disallow updating default group * move manage users and groups to navbar dropdown * show only addable apps and users on dropdowns * rename header as profile settings * scope addable apps and users by organization * scope viewable apps on homepage * hide manage groups link from non admins * make permissions to be used with radio input * add loading state for add apps/users buttons * revise unit tests * revise migrations * fix e2e tests * comment out dead code * fix seeds script * handle folder count * captalize error toast * hide manage users dropdown for non admins * show fobidden error on blank homepage * fix folder app count * fix invalid state set * make group name clickable for edit instead * users with edit permission can deploy apps * not show edit link on homepage if user dont have update permission * remove unused entity from merge * remove roles usage from manage org users page * fix folder count and blank slate on homepage * disable add buttons if there is no selections * humanize default groups on view * make app added onto groups have read permission by default * not show app menu if user is not admin * remove admin users from group user addition dropdown * create default permissions for app cloned * fix querying index page without page params * fix admin scoped out from group add * remove apps from header * fix invitation url not shown * scope admin deletion check by org * scope public apps by organization * add specs for group permissions e2e * removed unused entity and add group permissions spec * remove console logs * remove unused permission * scope public app count by org * remove console log * refactor manage group permission resources component * update group permssion in org scope
2021-10-11 15:15:58 +00:00
password,
forgotPasswordToken: null,
});
2021-07-26 16:02:47 +00:00
}
2021-07-19 05:42:16 +00:00
}
2021-07-08 07:39:07 +00:00
}