Browse Source

fix: handle invalid user object while preparing data

Signed-off-by: mertmit <mertmit99@gmail.com>
pull/7432/head
mertmit 8 months ago
parent
commit
31ab575745
  1. 283
      packages/nocodb/src/db/BaseModelSqlv2.ts

283
packages/nocodb/src/db/BaseModelSqlv2.ts

@ -20,6 +20,7 @@ import Validator from 'validator';
import { customAlphabet } from 'nanoid'; import { customAlphabet } from 'nanoid';
import DOMPurify from 'isomorphic-dompurify'; import DOMPurify from 'isomorphic-dompurify';
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { Logger } from '@nestjs/common';
import type { SortType } from 'nocodb-sdk'; import type { SortType } from 'nocodb-sdk';
import type { Knex } from 'knex'; import type { Knex } from 'knex';
import type LookupColumn from '~/models/LookupColumn'; import type LookupColumn from '~/models/LookupColumn';
@ -75,6 +76,8 @@ dayjs.extend(utc);
dayjs.extend(timezone); dayjs.extend(timezone);
const logger = new Logger('BaseModelSqlv2');
const GROUP_COL = '__nc_group_id'; const GROUP_COL = '__nc_group_id';
const nanoidv2 = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 14); const nanoidv2 = customAlphabet('1234567890abcdefghijklmnopqrstuvwxyz', 14);
@ -220,7 +223,7 @@ class BaseModelSqlv2 {
} catch (e) { } catch (e) {
if (validateFormula || !haveFormulaColumn(await this.model.getColumns())) if (validateFormula || !haveFormulaColumn(await this.model.getColumns()))
throw e; throw e;
console.log(e); logger.log(e);
return this.readByPk(id, true); return this.readByPk(id, true);
} }
@ -295,7 +298,7 @@ class BaseModelSqlv2 {
} catch (e) { } catch (e) {
if (validateFormula || !haveFormulaColumn(await this.model.getColumns())) if (validateFormula || !haveFormulaColumn(await this.model.getColumns()))
throw e; throw e;
console.log(e); logger.log(e);
return this.findOne(args, true); return this.findOne(args, true);
} }
@ -430,7 +433,7 @@ class BaseModelSqlv2 {
} catch (e) { } catch (e) {
if (validateFormula || !haveFormulaColumn(await this.model.getColumns())) if (validateFormula || !haveFormulaColumn(await this.model.getColumns()))
throw e; throw e;
console.log(e); logger.log(e);
return this.list(args, { return this.list(args, {
ignoreViewFilterAndSort, ignoreViewFilterAndSort,
ignorePagination, ignorePagination,
@ -654,7 +657,7 @@ class BaseModelSqlv2 {
sanitize(column.id), sanitize(column.id),
]); ]);
} catch (e) { } catch (e) {
console.log(e); logger.log(e);
// return dummy select // return dummy select
selectQb = this.dbDriver.raw(`'ERR' as ??`, [ selectQb = this.dbDriver.raw(`'ERR' as ??`, [
sanitize(column.id), sanitize(column.id),
@ -874,7 +877,7 @@ class BaseModelSqlv2 {
sanitize(column.id), sanitize(column.id),
]); ]);
} catch (e) { } catch (e) {
console.log(e); logger.log(e);
// return dummy select // return dummy select
selectQb = this.dbDriver.raw(`'ERR' as ??`, [ selectQb = this.dbDriver.raw(`'ERR' as ??`, [
sanitize(column.id), sanitize(column.id),
@ -1046,8 +1049,7 @@ class BaseModelSqlv2 {
GROUP_COL, GROUP_COL,
); );
} catch (e) { } catch (e) {
console.log(e); logger.error(e);
throw e;
} }
} }
@ -1114,7 +1116,6 @@ class BaseModelSqlv2 {
return children.map(({ count }) => count); return children.map(({ count }) => count);
} catch (e) { } catch (e) {
console.log(e);
throw e; throw e;
} }
} }
@ -1178,7 +1179,6 @@ class BaseModelSqlv2 {
return c; return c;
}); });
} catch (e) { } catch (e) {
console.log(e);
throw e; throw e;
} }
} }
@ -1219,7 +1219,6 @@ class BaseModelSqlv2 {
return (await this.execAndParse(query, null, { raw: true, first: true })) return (await this.execAndParse(query, null, { raw: true, first: true }))
?.count; ?.count;
} catch (e) { } catch (e) {
console.log(e);
throw e; throw e;
} }
} }
@ -2319,7 +2318,7 @@ class BaseModelSqlv2 {
]), ]),
); );
} catch (e) { } catch (e) {
console.log(e); logger.log(e);
// return dummy select // return dummy select
qb.select( qb.select(
this.dbDriver.raw(`'ERR' as ??`, [sanitize(column.id)]), this.dbDriver.raw(`'ERR' as ??`, [sanitize(column.id)]),
@ -2496,7 +2495,6 @@ class BaseModelSqlv2 {
await this.afterInsert(response, trx, cookie); await this.afterInsert(response, trx, cookie);
return Array.isArray(response) ? response[0] : response; return Array.isArray(response) ? response[0] : response;
} catch (e) { } catch (e) {
console.log(e);
await this.errorInsert(e, data, trx, cookie); await this.errorInsert(e, data, trx, cookie);
throw e; throw e;
} }
@ -2579,7 +2577,6 @@ class BaseModelSqlv2 {
await this.afterDelete(data, trx, cookie); await this.afterDelete(data, trx, cookie);
return response; return response;
} catch (e) { } catch (e) {
console.log(e);
if (!_trx) await trx.rollback(); if (!_trx) await trx.rollback();
await this.errorDelete(e, id, trx, cookie); await this.errorDelete(e, id, trx, cookie);
throw e; throw e;
@ -2681,7 +2678,6 @@ class BaseModelSqlv2 {
await this.afterUpdate(prevData, newData, trx, cookie, updateObj); await this.afterUpdate(prevData, newData, trx, cookie, updateObj);
return newData; return newData;
} catch (e) { } catch (e) {
console.log(e);
await this.errorUpdate(e, data, trx, cookie); await this.errorUpdate(e, data, trx, cookie);
throw e; throw e;
} }
@ -2869,7 +2865,6 @@ class BaseModelSqlv2 {
return response; return response;
} catch (e) { } catch (e) {
console.log(e);
throw e; throw e;
} }
} }
@ -3540,7 +3535,6 @@ class BaseModelSqlv2 {
return res; return res;
} catch (e) { } catch (e) {
if (transaction) await transaction.rollback(); if (transaction) await transaction.rollback();
console.log(e);
throw e; throw e;
} }
} }
@ -4474,7 +4468,6 @@ class BaseModelSqlv2 {
return r; return r;
} catch (e) { } catch (e) {
console.log(e);
throw e; throw e;
} }
} }
@ -5719,7 +5712,6 @@ class BaseModelSqlv2 {
} }
return parent; return parent;
} catch (e) { } catch (e) {
console.log(e);
throw e; throw e;
} }
} }
@ -5767,156 +5759,163 @@ class BaseModelSqlv2 {
} }
async prepareNocoData(data, isInsertData = false, cookie?: { user?: any }) { async prepareNocoData(data, isInsertData = false, cookie?: { user?: any }) {
if ( for (const column of this.model.columns) {
this.model.columns.some((c) => if (
[ ![
UITypes.Attachment, UITypes.Attachment,
UITypes.User, UITypes.User,
UITypes.CreatedTime, UITypes.CreatedTime,
UITypes.LastModifiedTime, UITypes.LastModifiedTime,
UITypes.CreatedBy, UITypes.CreatedBy,
UITypes.LastModifiedBy, UITypes.LastModifiedBy,
].includes(c.uidt), ].includes(column.uidt)
) )
) { continue;
for (const column of this.model.columns) {
if (column.system) { if (column.system) {
if (isInsertData) { if (isInsertData) {
if (column.uidt === UITypes.CreatedTime) { if (column.uidt === UITypes.CreatedTime) {
data[column.column_name] = Noco.ncMeta.now(); data[column.column_name] = Noco.ncMeta.now();
} else if (column.uidt === UITypes.CreatedBy) { } else if (column.uidt === UITypes.CreatedBy) {
data[column.column_name] = cookie?.user?.id; data[column.column_name] = cookie?.user?.id;
}
}
if (column.uidt === UITypes.LastModifiedTime) {
data[column.column_name] = isInsertData ? null : Noco.ncMeta.now();
} else if (column.uidt === UITypes.LastModifiedBy) {
data[column.column_name] = isInsertData ? null : cookie?.user?.id;
} }
} }
if (column.uidt === UITypes.Attachment) { if (column.uidt === UITypes.LastModifiedTime) {
if (data[column.column_name]) { data[column.column_name] = isInsertData ? null : Noco.ncMeta.now();
if (Array.isArray(data[column.column_name])) { } else if (column.uidt === UITypes.LastModifiedBy) {
for (let attachment of data[column.column_name]) { data[column.column_name] = isInsertData ? null : cookie?.user?.id;
attachment = extractProps(attachment, [ }
'url', }
'path', if (column.uidt === UITypes.Attachment) {
'title', if (data[column.column_name]) {
'mimetype', if (Array.isArray(data[column.column_name])) {
'size', for (let attachment of data[column.column_name]) {
'icon', attachment = extractProps(attachment, [
]); 'url',
} 'path',
'title',
'mimetype',
'size',
'icon',
]);
} }
} }
} else if ( }
[UITypes.User, UITypes.CreatedBy, UITypes.LastModifiedBy].includes( } else if (
column.uidt, [UITypes.User, UITypes.CreatedBy, UITypes.LastModifiedBy].includes(
) column.uidt,
) { )
if (data[column.column_name]) { ) {
const userIds = []; if (data[column.column_name]) {
const userIds = [];
if (typeof data[column.column_name] === 'string') { if (
try { typeof data[column.column_name] === 'string' &&
data[column.column_name] = JSON.parse(data[column.column_name]); /^\s*[{[]$/.test(data[column.column_name])
} catch (e) {} ) {
} try {
data[column.column_name] = JSON.parse(data[column.column_name]);
} catch (e) {}
}
const baseUsers = await BaseUser.getUsersList({ const baseUsers = await BaseUser.getUsersList({
base_id: this.model.base_id, base_id: this.model.base_id,
include_ws_deleted: false, include_ws_deleted: false,
}); });
if (typeof data[column.column_name] === 'string') { if (typeof data[column.column_name] === 'object') {
const users = data[column.column_name] const users: { id?: string; email?: string }[] = Array.isArray(
.split(',') data[column.column_name],
.map((u) => u.trim()); )
for (const user of users) { ? data[column.column_name]
try { : [data[column.column_name]];
if (user.length === 0) continue; for (const userObj of users) {
if (user.includes('@')) { const user = extractProps(userObj, ['id', 'email']);
const u = baseUsers.find((u) => u.email === user); try {
if (!u) { if ('id' in user) {
NcError.unprocessableEntity( const u = baseUsers.find((u) => u.id === user.id);
`User with email '${user}' is not part of this workspace`, if (!u) {
); NcError.unprocessableEntity(
} `User with id '${user.id}' is not part of this workspace`,
userIds.push(u.id); );
} else { }
const u = baseUsers.find((u) => u.id === user); userIds.push(u.id);
if (!u) { } else if ('email' in user) {
NcError.unprocessableEntity( // skip null input
`User with id '${user}' is not part of this workspace`, if (!user.email) continue;
); // trim extra spaces
} user.email = user.email.trim();
userIds.push(u.id); // skip empty input
if (user.email.length === 0) continue;
const u = baseUsers.find((u) => u.email === user.email);
if (!u) {
NcError.unprocessableEntity(
`User with email '${user.email}' is not part of this workspace`,
);
} }
} catch (e) { userIds.push(u.id);
NcError.unprocessableEntity(e.message); } else {
NcError.unprocessableEntity('Invalid user object');
} }
} catch (e) {
NcError.unprocessableEntity(e.message);
} }
} else { }
const users: { id?: string; email?: string }[] = Array.isArray( } else if (typeof data[column.column_name] === 'string') {
data[column.column_name], const users = data[column.column_name]
) .split(',')
? data[column.column_name] .map((u) => u.trim());
: [data[column.column_name]]; for (const user of users) {
for (const userObj of users) { try {
const user = extractProps(userObj, ['id', 'email']); if (user.length === 0) continue;
try { if (user.includes('@')) {
if ('id' in user) { const u = baseUsers.find((u) => u.email === user);
const u = baseUsers.find((u) => u.id === user.id); if (!u) {
if (!u) { NcError.unprocessableEntity(
NcError.unprocessableEntity( `User with email '${user}' is not part of this workspace`,
`User with id '${user.id}' is not part of this workspace`, );
); }
} userIds.push(u.id);
userIds.push(u.id); } else {
} else if ('email' in user) { const u = baseUsers.find((u) => u.id === user);
// skip null input if (!u) {
if (!user.email) continue; NcError.unprocessableEntity(
// trim extra spaces `User with id '${user}' is not part of this workspace`,
user.email = user.email.trim(); );
// skip empty input
if (user.email.length === 0) continue;
const u = baseUsers.find((u) => u.email === user.email);
if (!u) {
NcError.unprocessableEntity(
`User with email '${user.email}' is not part of this workspace`,
);
}
userIds.push(u.id);
} else {
NcError.unprocessableEntity('Invalid user object');
} }
} catch (e) { userIds.push(u.id);
NcError.unprocessableEntity(e.message);
} }
} catch (e) {
NcError.unprocessableEntity(e.message);
} }
} }
} else {
logger.error(
`${data[column.column_name]} is not a valid user input`,
);
NcError.unprocessableEntity('Invalid user object');
}
if (userIds.length === 0) { if (userIds.length === 0) {
data[column.column_name] = null; data[column.column_name] = null;
} else { } else {
const userSet = new Set(userIds); const userSet = new Set(userIds);
if (userSet.size !== userIds.length) {
NcError.unprocessableEntity(
'Duplicate users not allowed for user field',
);
}
if (userSet.size !== userIds.length) { if (column.meta?.is_multi) {
data[column.column_name] = userIds.join(',');
} else {
if (userIds.length > 1) {
NcError.unprocessableEntity( NcError.unprocessableEntity(
'Duplicate users not allowed for user field', `Multiple users not allowed for '${column.title}'`,
); );
}
if (column.meta?.is_multi) {
data[column.column_name] = userIds.join(',');
} else { } else {
if (userIds.length > 1) { data[column.column_name] = userIds[0];
NcError.unprocessableEntity(
`Multiple users not allowed for '${column.title}'`,
);
} else {
data[column.column_name] = userIds[0];
}
} }
} }
} }

Loading…
Cancel
Save