Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 21 additions & 4 deletions adminforth/dataConnectors/baseConnector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,17 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
}, { ok: true, error: '' });
}

if (!filters.operator) {
return { ok: false, error: `Field "operator" not specified in filter object: ${JSON.stringify(filters)}` };
}

if ((filters as IAdminForthSingleFilter).field) {
// if "field" is present, filter must be Single
if (!filters.operator) {
return { ok: false, error: `Field "operator" not specified in filter object: ${JSON.stringify(filters)}` };
}
if ((filters as IAdminForthSingleFilter).value === undefined) {
return { ok: false, error: `Field "value" not specified in filter object: ${JSON.stringify(filters)}` };
}
if ((filters as IAdminForthSingleFilter).insecureRawSQL) {
return { ok: false, error: `Field "insecureRawSQL" should not be specified in filter object alongside "field": ${JSON.stringify(filters)}` };
}
if (![AdminForthFilterOperators.EQ, AdminForthFilterOperators.NE, AdminForthFilterOperators.GT,
AdminForthFilterOperators.LT, AdminForthFilterOperators.GTE, AdminForthFilterOperators.LTE,
AdminForthFilterOperators.LIKE, AdminForthFilterOperators.ILIKE, AdminForthFilterOperators.IN,
Expand All @@ -73,6 +78,7 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
const similar = suggestIfTypo(resource.dataSourceColumns.map((col) => col.name), (filters as IAdminForthSingleFilter).field);
throw new Error(`Field '${(filters as IAdminForthSingleFilter).field}' not found in resource '${resource.resourceId}'. ${similar ? `Did you mean '${similar}'?` : ''}`);
}
// value normalization
if (filters.operator == AdminForthFilterOperators.IN || filters.operator == AdminForthFilterOperators.NIN) {
if (!Array.isArray(filters.value)) {
return { ok: false, error: `Value for operator '${filters.operator}' should be an array, in filter object: ${JSON.stringify(filters) }` };
Expand All @@ -85,8 +91,19 @@ export default class AdminForthBaseConnector implements IAdminForthDataSourceCon
} else {
(filters as IAdminForthSingleFilter).value = this.setFieldValue(fieldObj, (filters as IAdminForthSingleFilter).value);
}
} else if ((filters as IAdminForthSingleFilter).insecureRawSQL) {
// if "insecureRawSQL" filter is insecure sql string
if ((filters as IAdminForthSingleFilter).operator) {
return { ok: false, error: `Field "operator" should not be specified in filter object alongside "insecureRawSQL": ${JSON.stringify(filters)}` };
}
if ((filters as IAdminForthSingleFilter).value !== undefined) {
return { ok: false, error: `Field "value" should not be specified in filter object alongside "insecureRawSQL": ${JSON.stringify(filters)}` };
}
} else if ((filters as IAdminForthAndOrFilter).subFilters) {
// if "subFilters" is present, filter must be AndOr
if (!filters.operator) {
return { ok: false, error: `Field "operator" not specified in filter object: ${JSON.stringify(filters)}` };
}
if (![AdminForthFilterOperators.AND, AdminForthFilterOperators.OR].includes(filters.operator)) {
return { ok: false, error: `Field "operator" has wrong value in filter object: ${JSON.stringify(filters)}` };
}
Expand Down
19 changes: 18 additions & 1 deletion adminforth/dataConnectors/clickhouse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -185,9 +185,14 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
return `${field} ${operator} ${placeholder}`;
}

// filter is a single insecure raw sql
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
return (filter as IAdminForthSingleFilter).insecureRawSQL;
}

// filter is a AndOr filter
return (filter as IAdminForthAndOrFilter).subFilters.map((f) => {
if ((f as IAdminForthSingleFilter).field) {
if ((f as IAdminForthSingleFilter).field || (f as IAdminForthSingleFilter).insecureRawSQL) {
// subFilter is a Single filter
return this.getFilterString(resource, f);
}
Expand All @@ -209,6 +214,11 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
}
}

// filter is a Single insecure raw sql
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
return [];
}

// filter is a AndOrFilter
return (filter as IAdminForthAndOrFilter).subFilters.reduce((params: any[], f: IAdminForthSingleFilter | IAdminForthAndOrFilter) => {
return params.concat(this.getFilterParams(f));
Expand Down Expand Up @@ -310,6 +320,13 @@ class ClickhouseConnector extends AdminForthBaseConnector implements IAdminForth
filters: IAdminForthAndOrFilter;
}): Promise<number> {
const tableName = resource.table;
// validate and normalize in case this method is called from dataAPI
if (filters) {
const filterValidation = this.validateAndNormalizeFilters(filters, resource);
if (!filterValidation.ok) {
throw new Error(filterValidation.error);
}
}
const { where, params } = this.whereClause(resource, filters);

const countQ = await this.client.query({
Expand Down
13 changes: 11 additions & 2 deletions adminforth/dataConnectors/mongo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,10 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS
}

// filter is a AndOr filter
return this.OperatorsMap[filter.operator]((filter as IAdminForthAndOrFilter).subFilters.map((f) => this.getFilterQuery(resource, f)));
return this.OperatorsMap[filter.operator]((filter as IAdminForthAndOrFilter).subFilters
// mongodb should ignore raw sql
.filter((f) => (f as IAdminForthSingleFilter).insecureRawSQL === undefined)
.map((f) => this.getFilterQuery(resource, f)));
}

async getDataWithOriginalTypes({ resource, limit, offset, sort, filters }:
Expand Down Expand Up @@ -158,7 +161,13 @@ class MongoConnector extends AdminForthBaseConnector implements IAdminForthDataS
resource: AdminForthResource,
filters: IAdminForthAndOrFilter,
}): Promise<number> {

if (filters) {
// validate and normalize in case this method is called from dataAPI
const filterValidation = this.validateAndNormalizeFilters(filters, resource);
if (!filterValidation.ok) {
throw new Error(filterValidation.error);
}
}
const collection = this.client.db().collection(resource.table);
const query = filters.subFilters.length ? this.getFilterQuery(resource, filters) : {};
return await collection.countDocuments(query);
Expand Down
19 changes: 18 additions & 1 deletion adminforth/dataConnectors/mysql.ts
Original file line number Diff line number Diff line change
Expand Up @@ -186,9 +186,14 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
return `${field} ${operator} ${placeholder}`;
}

// filter is a single insecure raw sql
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
return (filter as IAdminForthSingleFilter).insecureRawSQL;
}

// filter is a AndOr filter
return (filter as IAdminForthAndOrFilter).subFilters.map((f) => {
if ((f as IAdminForthSingleFilter).field) {
if ((f as IAdminForthSingleFilter).field || (f as IAdminForthSingleFilter).insecureRawSQL) {
// subFilter is a Single filter
return this.getFilterString(f);
}
Expand All @@ -209,6 +214,11 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS
}
}

// filter is a Single insecure raw sql
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
return [];
}

// filter is a AndOrFilter
return (filter as IAdminForthAndOrFilter).subFilters.reduce((params: any[], f: IAdminForthSingleFilter | IAdminForthAndOrFilter) => {
return params.concat(this.getFilterParams(f));
Expand Down Expand Up @@ -252,6 +262,13 @@ class MysqlConnector extends AdminForthBaseConnector implements IAdminForthDataS

async getCount({ resource, filters }: { resource: AdminForthResource; filters: IAdminForthAndOrFilter; }): Promise<number> {
const tableName = resource.table;
// validate and normalize in case this method is called from dataAPI
if (filters) {
const filterValidation = this.validateAndNormalizeFilters(filters, resource);
if (!filterValidation.ok) {
throw new Error(filterValidation.error);
}
}
const { sql: where, values: filterValues } = this.whereClauseAndValues(filters);
const q = `SELECT COUNT(*) FROM ${tableName} ${where}`;
if (process.env.HEAVY_DEBUG_QUERY) {
Expand Down
19 changes: 18 additions & 1 deletion adminforth/dataConnectors/postgres.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,9 +219,14 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
return `${field} ${operator} ${placeholder}`;
}

// filter is a single insecure raw sql
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
return (filter as IAdminForthSingleFilter).insecureRawSQL;
}

// filter is a AndOr filter
return (filter as IAdminForthAndOrFilter).subFilters.map((f) => {
if ((f as IAdminForthSingleFilter).field) {
if ((f as IAdminForthSingleFilter).field || (f as IAdminForthSingleFilter).insecureRawSQL) {
// subFilter is a Single filter
return this.getFilterString(resource, f);
}
Expand All @@ -243,6 +248,11 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa
}
}

// filter is a single insecure raw sql
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
return [];
}

// filter is a AndOrFilter
return (filter as IAdminForthAndOrFilter).subFilters.reduce((params: any[], f: IAdminForthSingleFilter | IAdminForthAndOrFilter) => {
return params.concat(this.getFilterParams(f));
Expand Down Expand Up @@ -291,6 +301,13 @@ class PostgresConnector extends AdminForthBaseConnector implements IAdminForthDa

async getCount({ resource, filters }: { resource: AdminForthResource; filters: IAdminForthAndOrFilter; }): Promise<number> {
const tableName = resource.table;
// validate and normalize in case this method is called from dataAPI
if (filters) {
const filterValidation = this.validateAndNormalizeFilters(filters, resource);
if (!filterValidation.ok) {
throw new Error(filterValidation.error);
}
}
const { sql: where, values: filterValues } = this.whereClauseAndValues(resource, filters);
const q = `SELECT COUNT(*) FROM "${tableName}" ${where}`;
if (process.env.HEAVY_DEBUG_QUERY) {
Expand Down
13 changes: 12 additions & 1 deletion adminforth/dataConnectors/sqlite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,14 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
return `${field} ${operator} ${placeholder}`;
}

// filter is a single insecure raw sql
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
return (filter as IAdminForthSingleFilter).insecureRawSQL;
}

// filter is a AndOr filter
return (filter as IAdminForthAndOrFilter).subFilters.map((f) => {
if ((f as IAdminForthSingleFilter).field) {
if ((f as IAdminForthSingleFilter).field || (f as IAdminForthSingleFilter).insecureRawSQL) {
// subFilter is a Single filter
return this.getFilterString(f);
}
Expand All @@ -195,6 +200,11 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData
}
}

// filter is a Single insecure raw sql
if ((filter as IAdminForthSingleFilter).insecureRawSQL) {
return [];
}

// filter is a AndOrFilter
return (filter as IAdminForthAndOrFilter).subFilters.reduce((params: any[], f: IAdminForthSingleFilter | IAdminForthAndOrFilter) => {
return params.concat(this.getFilterParams(f));
Expand Down Expand Up @@ -234,6 +244,7 @@ class SQLiteConnector extends AdminForthBaseConnector implements IAdminForthData

async getCount({ resource, filters }) {
if (filters) {
// validate and normalize in case this method is called from dataAPI
const filterValidation = this.validateAndNormalizeFilters(filters, resource);
if (!filterValidation.ok) {
throw new Error(filterValidation.error);
Expand Down
7 changes: 4 additions & 3 deletions adminforth/types/Back.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,13 @@ export interface IExpressHttpServer extends IHttpServer {


export interface IAdminForthSingleFilter {
field: string;
operator: AdminForthFilterOperators.EQ | AdminForthFilterOperators.NE
field?: string;
operator?: AdminForthFilterOperators.EQ | AdminForthFilterOperators.NE
| AdminForthFilterOperators.GT | AdminForthFilterOperators.LT | AdminForthFilterOperators.GTE
| AdminForthFilterOperators.LTE | AdminForthFilterOperators.LIKE | AdminForthFilterOperators.ILIKE
| AdminForthFilterOperators.IN | AdminForthFilterOperators.NIN;
value: any;
value?: any;
insecureRawSQL?: string;
}
export interface IAdminForthAndOrFilter {
operator: AdminForthFilterOperators.AND | AdminForthFilterOperators.OR;
Expand Down