Browse Source

feat(api): resend invite api

Signed-off-by: Pranav C <pranavxc@gmail.com>
pull/4134/head
Pranav C 2 years ago
parent
commit
4aca2992dc
  1. 174
      packages/nocodb/src/lib/meta/api/orgUserApis.ts

174
packages/nocodb/src/lib/meta/api/orgUserApis.ts

@ -1,56 +1,58 @@
import { Router } from 'express' import { Router } from 'express';
import { v4 as uuidv4 } from 'uuid' import { PluginCategory } from 'nocodb-sdk';
import validator from 'validator' import { v4 as uuidv4 } from 'uuid';
import { OrgUserRoles } from '../../../enums/OrgUserRoles' import validator from 'validator';
import Audit from '../../models/Audit' import { OrgUserRoles } from '../../../enums/OrgUserRoles';
import ProjectUser from '../../models/ProjectUser' import Audit from '../../models/Audit';
import SyncSource from '../../models/SyncSource' import ProjectUser from '../../models/ProjectUser';
import User from '../../models/User' import SyncSource from '../../models/SyncSource';
import Noco from '../../Noco' import User from '../../models/User';
import { metaApiMetrics } from '../helpers/apiMetrics' import Noco from '../../Noco';
import { NcError } from '../helpers/catchError' import { MetaTable } from '../../utils/globals';
import { extractProps } from '../helpers/extractProps' import { metaApiMetrics } from '../helpers/apiMetrics';
import ncMetaAclMw from '../helpers/ncMetaAclMw' import { NcError } from '../helpers/catchError';
import { PagedResponseImpl } from '../helpers/PagedResponse' import { extractProps } from '../helpers/extractProps';
import { randomTokenString } from '../helpers/stringHelpers' import ncMetaAclMw from '../helpers/ncMetaAclMw';
import { Tele } from 'nc-help' import { PagedResponseImpl } from '../helpers/PagedResponse';
import { sendInviteEmail } from './projectUserApis' import { randomTokenString } from '../helpers/stringHelpers';
import { Tele } from 'nc-help';
import { sendInviteEmail } from './projectUserApis';
async function userList(req, res) { async function userList(req, res) {
res.json( res.json(
new PagedResponseImpl(await User.list(req.query), { new PagedResponseImpl(await User.list(req.query), {
...req.query, ...req.query,
count: await User.count(req.query), count: await User.count(req.query),
}), })
) );
} }
async function userUpdate(req, res) { async function userUpdate(req, res) {
const updateBody = extractProps(req.body, ['roles']) const updateBody = extractProps(req.body, ['roles']);
const user = await User.get(req.params.userId) const user = await User.get(req.params.userId);
if (user.roles.includes(OrgUserRoles.SUPER)) { if (user.roles.includes(OrgUserRoles.SUPER)) {
NcError.badRequest('Cannot update super admin roles') NcError.badRequest('Cannot update super admin roles');
} }
res.json(await User.update(req.params.userId, updateBody)) res.json(await User.update(req.params.userId, updateBody));
} }
async function userDelete(req, res) { async function userDelete(req, res) {
const ncMeta = await Noco.ncMeta.startTransaction() const ncMeta = await Noco.ncMeta.startTransaction();
try { try {
const user = await User.get(req.params.userId, ncMeta) const user = await User.get(req.params.userId, ncMeta);
if (user.roles.includes(OrgUserRoles.SUPER)) { if (user.roles.includes(OrgUserRoles.SUPER)) {
NcError.badRequest('Cannot delete super admin') NcError.badRequest('Cannot delete super admin');
} }
// delete project user entry and assign to super admin // delete project user entry and assign to super admin
const projectUsers = await ProjectUser.getProjectsList( const projectUsers = await ProjectUser.getProjectsList(
req.params.userId, req.params.userId,
ncMeta, ncMeta
) );
// TODO: assign super admin as project owner // TODO: assign super admin as project owner
for (const projectUser of projectUsers) { for (const projectUser of projectUsers) {
@ -62,17 +64,17 @@ async function userDelete(req, res) {
} }
// delete sync source entry // delete sync source entry
await SyncSource.deleteByUserId(req.params.userId, ncMeta) await SyncSource.deleteByUserId(req.params.userId, ncMeta);
// delete user // delete user
await User.delete(req.params.userId, ncMeta) await User.delete(req.params.userId, ncMeta);
await ncMeta.commit() await ncMeta.commit();
} catch (e) { } catch (e) {
await ncMeta.rollback(e) await ncMeta.rollback(e);
} }
res.json(await User.delete(req.params.userId)) res.json(await User.delete(req.params.userId));
} }
async function userAdd(req, res, next) { async function userAdd(req, res, next) {
@ -81,34 +83,34 @@ async function userAdd(req, res, next) {
req.body.roles && req.body.roles &&
![OrgUserRoles.VIEWER, OrgUserRoles.CREATOR].includes(req.body.roles) ![OrgUserRoles.VIEWER, OrgUserRoles.CREATOR].includes(req.body.roles)
) { ) {
NcError.badRequest('Invalid role') NcError.badRequest('Invalid role');
} }
// extract emails from request body // extract emails from request body
const emails = (req.body.email || '') const emails = (req.body.email || '')
.toLowerCase() .toLowerCase()
.split(/\s*,\s*/) .split(/\s*,\s*/)
.map((v) => v.trim()) .map((v) => v.trim());
// check for invalid emails // check for invalid emails
const invalidEmails = emails.filter((v) => !validator.isEmail(v)) const invalidEmails = emails.filter((v) => !validator.isEmail(v));
if (!emails.length) { if (!emails.length) {
return NcError.badRequest('Invalid email address') return NcError.badRequest('Invalid email address');
} }
if (invalidEmails.length) { if (invalidEmails.length) {
NcError.badRequest('Invalid email address : ' + invalidEmails.join(', ')) NcError.badRequest('Invalid email address : ' + invalidEmails.join(', '));
} }
const invite_token = uuidv4() const invite_token = uuidv4();
const error = [] const error = [];
for (const email of emails) { for (const email of emails) {
// add user to project if user already exist // add user to project if user already exist
const user = await User.getByEmail(email) const user = await User.getByEmail(email);
if (user) { if (user) {
NcError.badRequest('User already exist') NcError.badRequest('User already exist');
} else { } else {
try { try {
// create new user with invite token // create new user with invite token
@ -118,35 +120,34 @@ async function userAdd(req, res, next) {
email, email,
roles: OrgUserRoles.VIEWER, roles: OrgUserRoles.VIEWER,
token_version: randomTokenString(), token_version: randomTokenString(),
}) });
const count = await User.count() const count = await User.count();
Tele.emit('evt', { evt_type: 'org:user:invite', count }) Tele.emit('evt', { evt_type: 'org:user:invite', count });
await Audit.insert({ await Audit.insert({
project_id: req.params.projectId, op_type: 'ORG_USER',
op_type: 'AUTHENTICATION',
op_sub_type: 'INVITE', op_sub_type: 'INVITE',
user: req.user.email, user: req.user.email,
description: `invited ${email} to ${req.params.projectId} project `, description: `invited ${email} to ${req.params.projectId} project `,
ip: req.clientIp, ip: req.clientIp,
}) });
// in case of single user check for smtp failure // in case of single user check for smtp failure
// and send back token if failed // and send back token if failed
if ( if (
emails.length === 1 && emails.length === 1 &&
!(await sendInviteEmail(email, invite_token, req)) !(await sendInviteEmail(email, invite_token, req))
) { ) {
return res.json({ invite_token, email }) return res.json({ invite_token, email });
} else { } else {
sendInviteEmail(email, invite_token, req) sendInviteEmail(email, invite_token, req);
} }
} catch (e) { } catch (e) {
console.log(e) console.log(e);
if (emails.length === 1) { if (emails.length === 1) {
return next(e) return next(e);
} else { } else {
error.push({ email, error: e.message }) error.push({ email, error: e.message });
} }
} }
} }
@ -155,31 +156,74 @@ async function userAdd(req, res, next) {
if (emails.length === 1) { if (emails.length === 1) {
res.json({ res.json({
msg: 'success', msg: 'success',
}) });
} else { } else {
return res.json({ invite_token, emails, error }) return res.json({ invite_token, emails, error });
}
}
async function userInviteResend(req, res): Promise<any> {
const user = await User.get(req.params.userId);
if (!user) {
NcError.badRequest(`User with id '${req.params.userId}' not found`);
}
const invite_token = uuidv4();
await User.update(user.id, {
invite_token,
invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000),
});
const pluginData = await Noco.ncMeta.metaGet2(null, null, MetaTable.PLUGIN, {
category: PluginCategory.EMAIL,
active: true,
});
if (!pluginData) {
NcError.badRequest(
`No Email Plugin is found. Please go to App Store to configure first or copy the invitation URL to users instead.`
);
} }
await sendInviteEmail(user.email, invite_token, req);
await Audit.insert({
op_type: 'ORG_USER',
op_sub_type: 'RESEND_INVITE',
user: user.email,
description: `resent a invite to ${user.email} `,
ip: req.clientIp,
});
res.json({ msg: 'success' });
} }
const router = Router({ mergeParams: true }) const router = Router({ mergeParams: true });
router.get( router.get(
'/api/v1/users', '/api/v1/users',
metaApiMetrics, metaApiMetrics,
ncMetaAclMw(userList, 'userList', [OrgUserRoles.SUPER]), ncMetaAclMw(userList, 'userList', [OrgUserRoles.SUPER])
) );
router.patch( router.patch(
'/api/v1/users/:userId', '/api/v1/users/:userId',
metaApiMetrics, metaApiMetrics,
ncMetaAclMw(userUpdate, 'userUpdate', [OrgUserRoles.SUPER]), ncMetaAclMw(userUpdate, 'userUpdate', [OrgUserRoles.SUPER])
) );
router.delete( router.delete(
'/api/v1/users/:userId', '/api/v1/users/:userId',
metaApiMetrics, metaApiMetrics,
ncMetaAclMw(userDelete, 'userAdd', [OrgUserRoles.SUPER]), ncMetaAclMw(userDelete, 'userAdd', [OrgUserRoles.SUPER])
) );
router.post( router.post(
'/api/v1/users', '/api/v1/users',
metaApiMetrics, metaApiMetrics,
ncMetaAclMw(userAdd, 'userDelete', [OrgUserRoles.SUPER]), ncMetaAclMw(userAdd, 'userDelete', [OrgUserRoles.SUPER])
) );
export default router router.post(
'/api/v1/users/:userId/resend-invite',
metaApiMetrics,
ncMetaAclMw(userInviteResend, 'userInviteResend', [OrgUserRoles.SUPER])
);
export default router;

Loading…
Cancel
Save