mirror of https://github.com/nocodb/nocodb
Pranav C
2 years ago
2 changed files with 267 additions and 0 deletions
@ -0,0 +1,266 @@ |
|||||||
|
import { |
||||||
|
AuditOperationSubTypes, |
||||||
|
AuditOperationTypes, |
||||||
|
PluginCategory, |
||||||
|
UserType, |
||||||
|
} from 'nocodb-sdk'; |
||||||
|
import { v4 as uuidv4 } from 'uuid'; |
||||||
|
import validator from 'validator'; |
||||||
|
import { OrgUserRoles } from 'nocodb-sdk'; |
||||||
|
import { NC_APP_SETTINGS } from '../constants'; |
||||||
|
import { Audit, ProjectUser, Store, SyncSource, User } from '../models'; |
||||||
|
import Noco from '../Noco'; |
||||||
|
import { MetaTable } from '../utils/globals'; |
||||||
|
import { Tele } from 'nc-help'; |
||||||
|
import { NcError } from '../meta/helpers/catchError'; |
||||||
|
import { extractProps } from '../meta/helpers/extractProps'; |
||||||
|
import { PagedResponseImpl } from '../meta/helpers/PagedResponse'; |
||||||
|
import { randomTokenString } from '../meta/helpers/stringHelpers'; |
||||||
|
import { sendInviteEmail } from '../meta/api/projectUserApis'; |
||||||
|
|
||||||
|
export async function userList(param: { |
||||||
|
// todo: add better typing
|
||||||
|
query: Record<string, any>; |
||||||
|
}) { |
||||||
|
const { query = {} } = param; |
||||||
|
|
||||||
|
return new PagedResponseImpl(await User.list(query), { |
||||||
|
...query, |
||||||
|
count: await User.count(query), |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export async function userUpdate(param: { |
||||||
|
// todo: better typing
|
||||||
|
user: Partial<UserType>; |
||||||
|
userId: string; |
||||||
|
}) { |
||||||
|
const updateBody = extractProps(param.user, ['roles']); |
||||||
|
|
||||||
|
const user = await User.get(param.userId); |
||||||
|
|
||||||
|
if (user.roles.includes(OrgUserRoles.SUPER_ADMIN)) { |
||||||
|
NcError.badRequest('Cannot update super admin roles'); |
||||||
|
} |
||||||
|
|
||||||
|
return await User.update(param.userId, { |
||||||
|
...updateBody, |
||||||
|
token_version: null, |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
export async function userDelete(param: { userId: string }) { |
||||||
|
const ncMeta = await Noco.ncMeta.startTransaction(); |
||||||
|
try { |
||||||
|
const user = await User.get(param.userId, ncMeta); |
||||||
|
|
||||||
|
if (user.roles.includes(OrgUserRoles.SUPER_ADMIN)) { |
||||||
|
NcError.badRequest('Cannot delete super admin'); |
||||||
|
} |
||||||
|
|
||||||
|
// delete project user entry and assign to super admin
|
||||||
|
const projectUsers = await ProjectUser.getProjectsIdList( |
||||||
|
param.userId, |
||||||
|
ncMeta |
||||||
|
); |
||||||
|
|
||||||
|
// todo: clear cache
|
||||||
|
|
||||||
|
// TODO: assign super admin as project owner
|
||||||
|
for (const projectUser of projectUsers) { |
||||||
|
await ProjectUser.delete( |
||||||
|
projectUser.project_id, |
||||||
|
projectUser.fk_user_id, |
||||||
|
ncMeta |
||||||
|
); |
||||||
|
} |
||||||
|
|
||||||
|
// delete sync source entry
|
||||||
|
await SyncSource.deleteByUserId(param.userId, ncMeta); |
||||||
|
|
||||||
|
// delete user
|
||||||
|
await User.delete(param.userId, ncMeta); |
||||||
|
await ncMeta.commit(); |
||||||
|
} catch (e) { |
||||||
|
await ncMeta.rollback(e); |
||||||
|
throw e; |
||||||
|
} |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
export async function userAdd(param: { |
||||||
|
user: UserType; |
||||||
|
projectId: string; |
||||||
|
// todo: refactor
|
||||||
|
req: any; |
||||||
|
}) { |
||||||
|
// allow only viewer or creator role
|
||||||
|
if ( |
||||||
|
param.user.roles && |
||||||
|
![OrgUserRoles.VIEWER, OrgUserRoles.CREATOR].includes( |
||||||
|
param.user.roles as OrgUserRoles |
||||||
|
) |
||||||
|
) { |
||||||
|
NcError.badRequest('Invalid role'); |
||||||
|
} |
||||||
|
|
||||||
|
// extract emails from request body
|
||||||
|
const emails = (param.user.email || '') |
||||||
|
.toLowerCase() |
||||||
|
.split(/\s*,\s*/) |
||||||
|
.map((v) => v.trim()); |
||||||
|
|
||||||
|
// check for invalid emails
|
||||||
|
const invalidEmails = emails.filter((v) => !validator.isEmail(v)); |
||||||
|
|
||||||
|
if (!emails.length) { |
||||||
|
return NcError.badRequest('Invalid email address'); |
||||||
|
} |
||||||
|
if (invalidEmails.length) { |
||||||
|
NcError.badRequest('Invalid email address : ' + invalidEmails.join(', ')); |
||||||
|
} |
||||||
|
|
||||||
|
const invite_token = uuidv4(); |
||||||
|
const error = []; |
||||||
|
|
||||||
|
for (const email of emails) { |
||||||
|
// add user to project if user already exist
|
||||||
|
const user = await User.getByEmail(email); |
||||||
|
|
||||||
|
if (user) { |
||||||
|
NcError.badRequest('User already exist'); |
||||||
|
} else { |
||||||
|
try { |
||||||
|
// create new user with invite token
|
||||||
|
await User.insert({ |
||||||
|
invite_token, |
||||||
|
invite_token_expires: new Date(Date.now() + 24 * 60 * 60 * 1000), |
||||||
|
email, |
||||||
|
roles: param.user.roles || OrgUserRoles.VIEWER, |
||||||
|
token_version: randomTokenString(), |
||||||
|
}); |
||||||
|
|
||||||
|
const count = await User.count(); |
||||||
|
Tele.emit('evt', { evt_type: 'org:user:invite', count }); |
||||||
|
|
||||||
|
await Audit.insert({ |
||||||
|
op_type: AuditOperationTypes.ORG_USER, |
||||||
|
op_sub_type: AuditOperationSubTypes.INVITE, |
||||||
|
user: param.req.user.email, |
||||||
|
description: `invited ${email} to ${param.projectId} project `, |
||||||
|
ip: param.req.clientIp, |
||||||
|
}); |
||||||
|
// in case of single user check for smtp failure
|
||||||
|
// and send back token if failed
|
||||||
|
if ( |
||||||
|
emails.length === 1 && |
||||||
|
!(await sendInviteEmail(email, invite_token, param.req)) |
||||||
|
) { |
||||||
|
return { invite_token, email }; |
||||||
|
} else { |
||||||
|
sendInviteEmail(email, invite_token, param.req); |
||||||
|
} |
||||||
|
} catch (e) { |
||||||
|
console.log(e); |
||||||
|
if (emails.length === 1) { |
||||||
|
throw e; |
||||||
|
} else { |
||||||
|
error.push({ email, error: e.message }); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (emails.length === 1) { |
||||||
|
return { |
||||||
|
msg: 'success', |
||||||
|
}; |
||||||
|
} else { |
||||||
|
return { invite_token, emails, error }; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
export async function userSettings(_param): Promise<any> { |
||||||
|
NcError.notImplemented(); |
||||||
|
} |
||||||
|
|
||||||
|
export async function userInviteResend(param: { |
||||||
|
userId: string; |
||||||
|
req: any; |
||||||
|
}): Promise<any> { |
||||||
|
const user = await User.get(param.userId); |
||||||
|
|
||||||
|
if (!user) { |
||||||
|
NcError.badRequest(`User with id '${param.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, param.req); |
||||||
|
|
||||||
|
await Audit.insert({ |
||||||
|
op_type: AuditOperationTypes.ORG_USER, |
||||||
|
op_sub_type: AuditOperationSubTypes.RESEND_INVITE, |
||||||
|
user: user.email, |
||||||
|
description: `resent a invite to ${user.email} `, |
||||||
|
ip: param.req.clientIp, |
||||||
|
}); |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
export async function generateResetUrl(param: { |
||||||
|
userId: string; |
||||||
|
siteUrl: string; |
||||||
|
}) { |
||||||
|
const user = await User.get(param.userId); |
||||||
|
|
||||||
|
if (!user) { |
||||||
|
NcError.badRequest(`User with id '${param.userId}' not found`); |
||||||
|
} |
||||||
|
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, |
||||||
|
}); |
||||||
|
|
||||||
|
return { |
||||||
|
reset_password_token: token, |
||||||
|
reset_password_url: param.siteUrl + `/auth/password/reset/${token}`, |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
export async function appSettingsGet() { |
||||||
|
let settings = {}; |
||||||
|
try { |
||||||
|
settings = JSON.parse((await Store.get(NC_APP_SETTINGS))?.value); |
||||||
|
} catch {} |
||||||
|
return settings; |
||||||
|
} |
||||||
|
|
||||||
|
export async function appSettingsSet(param: { settings: any }) { |
||||||
|
await Store.saveOrUpdate({ |
||||||
|
value: JSON.stringify(param.settings), |
||||||
|
key: NC_APP_SETTINGS, |
||||||
|
}); |
||||||
|
return true; |
||||||
|
} |
Loading…
Reference in new issue