mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-18 02:21:13 +00:00
* feat(cli): Implement users account quota guards Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Remove comment Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Address PR comments - Getting `usersQuota` from `Settings` repo - Revert `isUserManagementEnabled` helper - Fix FE listing of users Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Refactor isWithinUserQuota getter and fix tests Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Revert testDb.ts changes Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Cleanup & improve types Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Fix duplicated method * Fix failing test * Remove `isUserManagementEnabled` completely Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Check for globalRole.name to determine if user is owner Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Fix unit tests Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Set isInstanceOwnerSetUp in specs * Fix SettingsUserView UM Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * refactor: License typings suggestions for users quota guards (#6636) refactor: License typings suggestions * Update packages/cli/src/Ldap/helpers.ts Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * Update packages/cli/test/integration/shared/utils.ts Co-authored-by: Iván Ovejero <ivov.src@gmail.com> * Address PR comments Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> * Use 403 for all user quota related errors Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> --------- Signed-off-by: Oleg Ivaniv <me@olegivaniv.com> Co-authored-by: Iván Ovejero <ivov.src@gmail.com>
92 lines
2.6 KiB
TypeScript
92 lines
2.6 KiB
TypeScript
/* eslint-disable @typescript-eslint/no-non-null-assertion */
|
|
|
|
import jwt from 'jsonwebtoken';
|
|
import type { Response } from 'express';
|
|
import { createHash } from 'crypto';
|
|
import * as Db from '@/Db';
|
|
import { AUTH_COOKIE_NAME, RESPONSE_ERROR_MESSAGES } from '@/constants';
|
|
import type { JwtPayload, JwtToken } from '@/Interfaces';
|
|
import type { User } from '@db/entities/User';
|
|
import config from '@/config';
|
|
import * as ResponseHelper from '@/ResponseHelper';
|
|
import { License } from '@/License';
|
|
import { Container } from 'typedi';
|
|
|
|
export function issueJWT(user: User): JwtToken {
|
|
const { id, email, password } = user;
|
|
const expiresIn = 7 * 86400000; // 7 days
|
|
const isWithinUsersLimit = Container.get(License).isWithinUsersLimit();
|
|
|
|
const payload: JwtPayload = {
|
|
id,
|
|
email,
|
|
password: password ?? null,
|
|
};
|
|
|
|
if (
|
|
config.getEnv('userManagement.isInstanceOwnerSetUp') &&
|
|
!user.isOwner &&
|
|
!isWithinUsersLimit
|
|
) {
|
|
throw new ResponseHelper.UnauthorizedError(RESPONSE_ERROR_MESSAGES.USERS_QUOTA_REACHED);
|
|
}
|
|
if (password) {
|
|
payload.password = createHash('sha256')
|
|
.update(password.slice(password.length / 2))
|
|
.digest('hex');
|
|
}
|
|
|
|
const signedToken = jwt.sign(payload, config.getEnv('userManagement.jwtSecret'), {
|
|
expiresIn: expiresIn / 1000 /* in seconds */,
|
|
algorithm: 'HS256',
|
|
});
|
|
|
|
return {
|
|
token: signedToken,
|
|
expiresIn,
|
|
};
|
|
}
|
|
|
|
export async function resolveJwtContent(jwtPayload: JwtPayload): Promise<User> {
|
|
const user = await Db.collections.User.findOne({
|
|
where: { id: jwtPayload.id },
|
|
relations: ['globalRole'],
|
|
});
|
|
|
|
let passwordHash = null;
|
|
if (user?.password) {
|
|
passwordHash = createHash('sha256')
|
|
.update(user.password.slice(user.password.length / 2))
|
|
.digest('hex');
|
|
}
|
|
|
|
// currently only LDAP users during synchronization
|
|
// can be set to disabled
|
|
if (user?.disabled) {
|
|
throw new ResponseHelper.AuthError('Unauthorized');
|
|
}
|
|
|
|
if (!user || jwtPayload.password !== passwordHash || user.email !== jwtPayload.email) {
|
|
// When owner hasn't been set up, the default user
|
|
// won't have email nor password (both equals null)
|
|
throw new Error('Invalid token content');
|
|
}
|
|
return user;
|
|
}
|
|
|
|
export async function resolveJwt(token: string): Promise<User> {
|
|
const jwtPayload = jwt.verify(token, config.getEnv('userManagement.jwtSecret'), {
|
|
algorithms: ['HS256'],
|
|
}) as JwtPayload;
|
|
return resolveJwtContent(jwtPayload);
|
|
}
|
|
|
|
export async function issueCookie(res: Response, user: User): Promise<void> {
|
|
const userData = issueJWT(user);
|
|
res.cookie(AUTH_COOKIE_NAME, userData.token, {
|
|
maxAge: userData.expiresIn,
|
|
httpOnly: true,
|
|
sameSite: 'lax',
|
|
});
|
|
}
|