Browse Source

refactor: update useApis content

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/5239/head
Pranav C 2 years ago
parent
commit
40689609fb
  1. 315
      packages/nocodb/src/lib/controllers/userController/userApis.ts

315
packages/nocodb/src/lib/controllers/userController/userApis.ts

@ -1,82 +1,24 @@
import { Request, Response } from 'express'; import { Request, Response } from 'express';
import { TableType, validatePassword } from 'nocodb-sdk'; import { TableType, validatePassword } from 'nocodb-sdk';
import { OrgUserRoles } from 'nocodb-sdk';
import { NC_APP_SETTINGS } from '../../../constants';
import Store from '../../../models/Store';
import { T } from 'nc-help'; import { T } from 'nc-help';
import catchError, { NcError } from '../../helpers/catchError';
const { isEmail } = require('validator'); const { isEmail } = require('validator');
import * as ejs from 'ejs'; import * as ejs from 'ejs';
import bcrypt from 'bcryptjs'; import bcrypt from 'bcryptjs';
import { promisify } from 'util'; import { promisify } from 'util';
import User from '../../../models/User';
const { v4: uuidv4 } = require('uuid'); const { v4: uuidv4 } = require('uuid');
import Audit from '../../../models/Audit';
import NcPluginMgrv2 from '../../helpers/NcPluginMgrv2';
import passport from 'passport'; import passport from 'passport';
import extractProjectIdAndAuthenticate from '../../helpers/extractProjectIdAndAuthenticate'; import { getAjvValidatorMw } from '../../meta/api/helpers';
import ncMetaAclMw from '../../helpers/ncMetaAclMw'; import catchError, { NcError } from '../../meta/helpers/catchError';
import { MetaTable } from '../../../utils/globals'; import extractProjectIdAndAuthenticate from '../../meta/helpers/extractProjectIdAndAuthenticate';
import Noco from '../../../Noco'; import ncMetaAclMw from '../../meta/helpers/ncMetaAclMw';
import { getAjvValidatorMw } from '../helpers'; import NcPluginMgrv2 from '../../meta/helpers/NcPluginMgrv2';
import { genJwt } from './helpers'; import { Audit, User } from '../../models';
import { randomTokenString } from '../../helpers/stringHelpers'; import Noco from '../../Noco';
import { userService } from '../../services';
export async function registerNewUserIfAllowed({
firstname,
lastname,
email,
salt,
password,
email_verification_token,
}: {
firstname;
lastname;
email: string;
salt: any;
password;
email_verification_token;
}) {
let roles: string = OrgUserRoles.CREATOR;
if (await User.isFirst()) {
roles = `${OrgUserRoles.CREATOR},${OrgUserRoles.SUPER_ADMIN}`;
// todo: update in nc_store
// roles = 'owner,creator,editor'
T.emit('evt', {
evt_type: 'project:invite',
count: 1,
});
} else {
let settings: { invite_only_signup?: boolean } = {};
try {
settings = JSON.parse((await Store.get(NC_APP_SETTINGS))?.value);
} catch {}
if (settings?.invite_only_signup) {
NcError.badRequest('Not allowed to signup, contact super admin.');
} else {
roles = OrgUserRoles.VIEWER;
}
}
const token_version = randomTokenString();
return await User.insert({
firstname,
lastname,
email,
salt,
password,
email_verification_token,
roles,
token_version,
});
}
export async function signup(req: Request, res: Response<TableType>) { export async function signup(req: Request, res: Response<TableType>) {
const { const {
@ -141,7 +83,7 @@ export async function signup(req: Request, res: Response<TableType>) {
NcError.badRequest('User already exist'); NcError.badRequest('User already exist');
} }
} else { } else {
await registerNewUserIfAllowed({ await userService.registerNewUserIfAllowed({
firstname, firstname,
lastname, lastname,
email, email,
@ -171,7 +113,7 @@ export async function signup(req: Request, res: Response<TableType>) {
); );
} }
await promisify((req as any).login.bind(req))(user); await promisify((req as any).login.bind(req))(user);
const refreshToken = randomTokenString(); const refreshToken = userService.randomTokenString();
await User.update(user.id, { await User.update(user.id, {
refresh_token: refreshToken, refresh_token: refreshToken,
email: user.email, email: user.email,
@ -190,18 +132,18 @@ export async function signup(req: Request, res: Response<TableType>) {
}); });
res.json({ res.json({
token: genJwt(user, Noco.getConfig()), token: userService.genJwt(user, Noco.getConfig()),
} as any); } as any);
} }
async function successfulSignIn({ async function successfulSignIn({
user, user,
err, err,
info, info,
req, req,
res, res,
auditDescription, auditDescription,
}) { }) {
try { try {
if (!user || !user.email) { if (!user || !user.email) {
if (err) { if (err) {
@ -214,10 +156,10 @@ async function successfulSignIn({
} }
await promisify((req as any).login.bind(req))(user); await promisify((req as any).login.bind(req))(user);
const refreshToken = randomTokenString(); const refreshToken = userService.randomTokenString();
if (!user.token_version) { if (!user.token_version) {
user.token_version = randomTokenString(); user.token_version = userService.randomTokenString();
} }
await User.update(user.id, { await User.update(user.id, {
@ -236,7 +178,7 @@ async function successfulSignIn({
}); });
res.json({ res.json({
token: genJwt(user, Noco.getConfig()), token: userService.genJwt(user, Noco.getConfig()),
} as any); } as any);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
@ -279,15 +221,13 @@ async function googleSignin(req, res, next) {
)(req, res, next); )(req, res, next);
} }
const REFRESH_TOKEN_COOKIE_KEY = 'refresh_token'; function setTokenCookie(res: Response, token): void {
function setTokenCookie(res, token): void {
// create http only cookie with refresh token that expires in 7 days // create http only cookie with refresh token that expires in 7 days
const cookieOptions = { const cookieOptions = {
httpOnly: true, httpOnly: true,
expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000), expires: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
}; };
res.cookie(REFRESH_TOKEN_COOKIE_KEY, token, cookieOptions); res.cookie('refresh_token', token, cookieOptions);
} }
async function me(req, res): Promise<any> { async function me(req, res): Promise<any> {
@ -298,184 +238,47 @@ async function passwordChange(req: Request<any, any>, res): Promise<any> {
if (!(req as any).isAuthenticated()) { if (!(req as any).isAuthenticated()) {
NcError.forbidden('Not allowed'); NcError.forbidden('Not allowed');
} }
const { currentPassword, newPassword } = req.body;
if (!currentPassword || !newPassword) {
return NcError.badRequest('Missing new/old password');
}
// validate password and throw error if password is satisfying the conditions
const { valid, error } = validatePassword(newPassword);
if (!valid) {
NcError.badRequest(`Password : ${error}`);
}
const user = await User.getByEmail((req as any).user.email); await userService.passwordChange({
const hashedPassword = await promisify(bcrypt.hash)( user: req['user'],
currentPassword, req,
user.salt body: req.body,
);
if (hashedPassword !== user.password) {
return NcError.badRequest('Current password is wrong');
}
const salt = await promisify(bcrypt.genSalt)(10);
const password = await promisify(bcrypt.hash)(newPassword, salt);
await User.update(user.id, {
salt,
password,
email: user.email,
token_version: null,
});
await Audit.insert({
op_type: 'AUTHENTICATION',
op_sub_type: 'PASSWORD_CHANGE',
user: user.email,
description: `changed password `,
ip: (req as any).clientIp,
}); });
res.json({ msg: 'Password updated successfully' }); res.json({ msg: 'Password updated successfully' });
} }
async function passwordForgot(req: Request<any, any>, res): Promise<any> { async function passwordForgot(req: Request<any, any>, res): Promise<any> {
const _email = req.body.email; await userService.passwordForgot({
if (!_email) { siteUrl: (req as any).ncSiteUrl,
NcError.badRequest('Please enter your email address.'); body: req.body,
} req,
});
const email = _email.toLowerCase();
const user = await User.getByEmail(email);
if (user) {
const token = uuidv4();
await User.update(user.id, {
email: user.email,
reset_password_token: token,
reset_password_expires: new Date(Date.now() + 60 * 60 * 1000),
token_version: null,
});
try {
const template = (await import('./ui/emailTemplates/forgotPassword'))
.default;
await NcPluginMgrv2.emailAdapter().then((adapter) =>
adapter.mailSend({
to: user.email,
subject: 'Password Reset Link',
text: `Visit following link to update your password : ${
(req as any).ncSiteUrl
}/auth/password/reset/${token}.`,
html: ejs.render(template, {
resetLink: (req as any).ncSiteUrl + `/auth/password/reset/${token}`,
}),
})
);
} catch (e) {
console.log(e);
return NcError.badRequest(
'Email Plugin is not found. Please contact administrators to configure it in App Store first.'
);
}
await Audit.insert({
op_type: 'AUTHENTICATION',
op_sub_type: 'PASSWORD_FORGOT',
user: user.email,
description: `requested for password reset `,
ip: (req as any).clientIp,
});
} else {
return NcError.badRequest('Your email has not been registered.');
}
res.json({ msg: 'Please check your email to reset the password' }); res.json({ msg: 'Please check your email to reset the password' });
} }
async function tokenValidate(req, res): Promise<any> { async function tokenValidate(req, res): Promise<any> {
const token = req.params.tokenId; await userService.tokenValidate({
token: req.params.tokenId,
const user = await Noco.ncMeta.metaGet(null, null, MetaTable.USERS, {
reset_password_token: token,
}); });
if (!user || !user.email) {
NcError.badRequest('Invalid reset url');
}
if (new Date(user.reset_password_expires) < new Date()) {
NcError.badRequest('Password reset url expired');
}
res.json(true); res.json(true);
} }
async function passwordReset(req, res): Promise<any> { async function passwordReset(req, res): Promise<any> {
const token = req.params.tokenId; await userService.passwordReset({
token: req.params.tokenId,
const user = await Noco.ncMeta.metaGet(null, null, MetaTable.USERS, { body: req.body,
reset_password_token: token, req,
});
if (!user) {
NcError.badRequest('Invalid reset url');
}
if (user.reset_password_expires < new Date()) {
NcError.badRequest('Password reset url expired');
}
if (user.provider && user.provider !== 'local') {
NcError.badRequest('Email registered via social account');
}
// validate password and throw error if password is satisfying the conditions
const { valid, error } = validatePassword(req.body.password);
if (!valid) {
NcError.badRequest(`Password : ${error}`);
}
const salt = await promisify(bcrypt.genSalt)(10);
const password = await promisify(bcrypt.hash)(req.body.password, salt);
await User.update(user.id, {
salt,
password,
email: user.email,
reset_password_expires: null,
reset_password_token: '',
token_version: null,
});
await Audit.insert({
op_type: 'AUTHENTICATION',
op_sub_type: 'PASSWORD_RESET',
user: user.email,
description: `did reset password `,
ip: req.clientIp,
}); });
res.json({ msg: 'Password reset successful' }); res.json({ msg: 'Password reset successful' });
} }
async function emailVerification(req, res): Promise<any> { async function emailVerification(req, res): Promise<any> {
const token = req.params.tokenId; await userService.emailVerification({
token: req.params.tokenId,
const user = await Noco.ncMeta.metaGet(null, null, MetaTable.USERS, { req,
email_verification_token: token,
});
if (!user) {
NcError.badRequest('Invalid verification url');
}
await User.update(user.id, {
email: user.email,
email_verification_token: '',
email_verified: true,
});
await Audit.insert({
op_type: 'AUTHENTICATION',
op_sub_type: 'EMAIL_VERIFICATION',
user: user.email,
description: `verified email `,
ip: req.clientIp,
}); });
res.json({ msg: 'Email verified successfully' }); res.json({ msg: 'Email verified successfully' });
@ -487,15 +290,13 @@ async function refreshToken(req, res): Promise<any> {
return res.status(400).json({ msg: 'Missing refresh token' }); return res.status(400).json({ msg: 'Missing refresh token' });
} }
const user = await User.getByRefreshToken( const user = await User.getByRefreshToken(req.cookies.refresh_token);
req.cookies[REFRESH_TOKEN_COOKIE_KEY]
);
if (!user) { if (!user) {
return res.status(400).json({ msg: 'Invalid refresh token' }); return res.status(400).json({ msg: 'Invalid refresh token' });
} }
const refreshToken = randomTokenString(); const refreshToken = userService.randomTokenString();
await User.update(user.id, { await User.update(user.id, {
email: user.email, email: user.email,
@ -505,7 +306,7 @@ async function refreshToken(req, res): Promise<any> {
setTokenCookie(res, refreshToken); setTokenCookie(res, refreshToken);
res.json({ res.json({
token: genJwt(user, Noco.getConfig()), token: userService.genJwt(user, Noco.getConfig()),
} as any); } as any);
} catch (e) { } catch (e) {
return res.status(400).json({ msg: e.message }); return res.status(400).json({ msg: e.message });
@ -526,30 +327,6 @@ async function renderPasswordReset(req, res): Promise<any> {
} }
} }
// clear refresh token cookie and update user refresh token to null
const signout = async (req, res): Promise<any> => {
const resBody = { msg: 'Success' };
if (!req.cookies[REFRESH_TOKEN_COOKIE_KEY]) {
return res.json(resBody);
}
const user = await User.getByRefreshToken(
req.cookies[REFRESH_TOKEN_COOKIE_KEY]
);
if (!user) {
return res.json(resBody);
}
res.clearCookie(REFRESH_TOKEN_COOKIE_KEY);
await User.update(user.id, {
refresh_token: null,
});
res.json(resBody);
};
const mapRoutes = (router) => { const mapRoutes = (router) => {
// todo: old api - /auth/signup?tool=1 // todo: old api - /auth/signup?tool=1
router.post( router.post(
@ -562,7 +339,6 @@ const mapRoutes = (router) => {
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'), getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'),
catchError(signin) catchError(signin)
); );
router.post('/auth/user/signout', catchError(signout));
router.get('/auth/user/me', extractProjectIdAndAuthenticate, catchError(me)); router.get('/auth/user/me', extractProjectIdAndAuthenticate, catchError(me));
router.post( router.post(
'/auth/password/forgot', '/auth/password/forgot',
@ -651,7 +427,6 @@ const mapRoutes = (router) => {
getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'), getAjvValidatorMw('swagger.json#/components/schemas/SignInReq'),
catchError(signin) catchError(signin)
); );
router.post('/api/v1/auth/user/signout', catchError(signout));
router.get( router.get(
'/api/v1/auth/user/me', '/api/v1/auth/user/me',
extractProjectIdAndAuthenticate, extractProjectIdAndAuthenticate,
@ -683,4 +458,4 @@ const mapRoutes = (router) => {
// respond with password reset page // respond with password reset page
router.get('/auth/password/reset/:tokenId', catchError(renderPasswordReset)); router.get('/auth/password/reset/:tokenId', catchError(renderPasswordReset));
}; };
export { mapRoutes as userApis }; export { mapRoutes as userController };

Loading…
Cancel
Save