不要怂,就是干,撸起袖子干!

Commit 14f72cdd by Simon Schick

refactor(querying): remove operatorAliases support (#10818)

BREAKING CHANGE:

Operator aliases were soft deprecated via the `opt-in` option
`operatorAlises` in v5 they have been entirely removed.

Please refer to previous changelogs for the migration guide.
1 parent f982257c
...@@ -241,84 +241,6 @@ const Op = Sequelize.Op; ...@@ -241,84 +241,6 @@ const Op = Sequelize.Op;
// title LIKE 'Boat%' OR description LIKE '%boat%' // title LIKE 'Boat%' OR description LIKE '%boat%'
``` ```
#### Operators Aliases
Sequelize allows setting specific strings as aliases for operators. With v5 this will give you deprecation warning.
```js
const Op = Sequelize.Op;
const operatorsAliases = {
$gt: Op.gt
}
const connection = new Sequelize(db, user, pass, { operatorsAliases })
[Op.gt]: 6 // > 6
$gt: 6 // same as using Op.gt (> 6)
```
#### Operators security
By default Sequelize will use Symbol operators. Using Sequelize without any aliases improves security. Not having any string aliases will make it extremely unlikely that operators could be injected but you should always properly validate and sanitize user input.
Some frameworks automatically parse user input into js objects and if you fail to sanitize your input it might be possible to inject an Object with string operators to Sequelize.
For better security it is highly advised to use symbol operators from `Sequelize.Op` like `Op.and` / `Op.or` in your code and not depend on any string based operators like `$and` / `$or` at all. You can limit alias your application will need by setting `operatorsAliases` option, remember to sanitize user input especially when you are directly passing them to Sequelize methods.
```js
const Op = Sequelize.Op;
//use sequelize without any operators aliases
const connection = new Sequelize(db, user, pass, { operatorsAliases: false });
//use sequelize with only alias for $and => Op.and
const connection2 = new Sequelize(db, user, pass, { operatorsAliases: { $and: Op.and } });
```
Sequelize will warn you if you're using the default aliases and not limiting them
if you want to keep using all default aliases (excluding legacy ones) without the warning you can pass the following operatorsAliases option -
```js
const Op = Sequelize.Op;
const operatorsAliases = {
$eq: Op.eq,
$ne: Op.ne,
$gte: Op.gte,
$gt: Op.gt,
$lte: Op.lte,
$lt: Op.lt,
$not: Op.not,
$in: Op.in,
$notIn: Op.notIn,
$is: Op.is,
$like: Op.like,
$notLike: Op.notLike,
$iLike: Op.iLike,
$notILike: Op.notILike,
$regexp: Op.regexp,
$notRegexp: Op.notRegexp,
$iRegexp: Op.iRegexp,
$notIRegexp: Op.notIRegexp,
$between: Op.between,
$notBetween: Op.notBetween,
$overlap: Op.overlap,
$contains: Op.contains,
$contained: Op.contained,
$adjacent: Op.adjacent,
$strictLeft: Op.strictLeft,
$strictRight: Op.strictRight,
$noExtendRight: Op.noExtendRight,
$noExtendLeft: Op.noExtendLeft,
$and: Op.and,
$or: Op.or,
$any: Op.any,
$all: Op.all,
$values: Op.values,
$col: Op.col
};
const connection = new Sequelize(db, user, pass, { operatorsAliases });
```
### JSON ### JSON
The JSON data type is supported by the PostgreSQL, SQLite, MySQL and MariaDB dialects only. The JSON data type is supported by the PostgreSQL, SQLite, MySQL and MariaDB dialects only.
......
...@@ -7,3 +7,9 @@ Sequelize v6 is the next major release after v4 ...@@ -7,3 +7,9 @@ Sequelize v6 is the next major release after v4
### Support for Node 8 and up ### Support for Node 8 and up
Sequelize v6 will only support Node 8 and up Sequelize v6 will only support Node 8 and up
### Removed support for `operatorAliases`
Operator aliases were soft deprecated via the `opt-in` option `operatorAlises` in v5 they have been entirely removed.
Please refer to previous changelogs for the migration guide.
...@@ -2070,10 +2070,6 @@ class QueryGenerator { ...@@ -2070,10 +2070,6 @@ class QueryGenerator {
const isPlainObject = _.isPlainObject(value); const isPlainObject = _.isPlainObject(value);
const isArray = !isPlainObject && Array.isArray(value); const isArray = !isPlainObject && Array.isArray(value);
key = this.OperatorsAliasMap && this.OperatorsAliasMap[key] || key;
if (isPlainObject) {
value = this._replaceAliases(value);
}
const valueKeys = isPlainObject && Utils.getComplexKeys(value); const valueKeys = isPlainObject && Utils.getComplexKeys(value);
if (key === undefined) { if (key === undefined) {
......
'use strict'; 'use strict';
const _ = require('lodash');
const Op = require('../../../operators'); const Op = require('../../../operators');
const Utils = require('../../../utils');
const OperatorHelpers = { const OperatorHelpers = {
OperatorMap: { OperatorMap: {
...@@ -43,41 +41,6 @@ const OperatorHelpers = { ...@@ -43,41 +41,6 @@ const OperatorHelpers = {
[Op.or]: ' OR ', [Op.or]: ' OR ',
[Op.col]: 'COL', [Op.col]: 'COL',
[Op.placeholder]: '$$PLACEHOLDER$$' [Op.placeholder]: '$$PLACEHOLDER$$'
},
OperatorsAliasMap: {},
setOperatorsAliases(aliases) {
if (!aliases || _.isEmpty(aliases)) {
this.OperatorsAliasMap = false;
} else {
this.OperatorsAliasMap = Object.assign({}, aliases);
}
},
_replaceAliases(orig) {
const obj = {};
if (!this.OperatorsAliasMap) {
return orig;
}
Utils.getOperators(orig).forEach(op => {
const item = orig[op];
if (_.isPlainObject(item)) {
obj[op] = this._replaceAliases(item);
} else {
obj[op] = item;
}
});
_.forOwn(orig, (item, prop) => {
prop = this.OperatorsAliasMap[prop] || prop;
if (_.isPlainObject(item)) {
item = this._replaceAliases(item);
}
obj[prop] = item;
});
return obj;
} }
}; };
......
...@@ -166,7 +166,6 @@ class Sequelize { ...@@ -166,7 +166,6 @@ class Sequelize {
* @param {Array} [options.retry.match] Only retry a query if the error matches one of these strings. * @param {Array} [options.retry.match] Only retry a query if the error matches one of these strings.
* @param {number} [options.retry.max] How many times a failing query is automatically retried. Set to 0 to disable retrying on SQL_BUSY error. * @param {number} [options.retry.max] How many times a failing query is automatically retried. Set to 0 to disable retrying on SQL_BUSY error.
* @param {boolean} [options.typeValidation=false] Run built in type validators on insert and update, e.g. validate that arguments passed to integer fields are integer-like. * @param {boolean} [options.typeValidation=false] Run built in type validators on insert and update, e.g. validate that arguments passed to integer fields are integer-like.
* @param {Object} [options.operatorsAliases] String based operator alias. Pass object to limit set of aliased operators.
* @param {Object} [options.hooks] An object of global hook functions that are called before and after certain lifecycle events. Global hooks will run after any model-specific hooks defined for the same event (See `Sequelize.Model.init()` for a list). Additionally, `beforeConnect()` and `afterConnect()` hooks may be defined here. * @param {Object} [options.hooks] An object of global hook functions that are called before and after certain lifecycle events. Global hooks will run after any model-specific hooks defined for the same event (See `Sequelize.Model.init()` for a list). Additionally, `beforeConnect()` and `afterConnect()` hooks may be defined here.
*/ */
constructor(database, username, password, options) { constructor(database, username, password, options) {
...@@ -320,11 +319,8 @@ class Sequelize { ...@@ -320,11 +319,8 @@ class Sequelize {
this.dialect = new Dialect(this); this.dialect = new Dialect(this);
this.dialect.QueryGenerator.typeValidation = options.typeValidation; this.dialect.QueryGenerator.typeValidation = options.typeValidation;
if (_.isPlainObject(this.options.operatorsAliases)) { if ('operatorsAliases' in this.options) {
deprecations.noStringOperators(); throw new Error('operatorAliases support was removed in v6');
this.dialect.QueryGenerator.setOperatorsAliases(this.options.operatorsAliases);
} else if (typeof this.options.operatorsAliases === 'boolean') {
deprecations.noBoolOperatorAliases();
} }
this.queryInterface = new QueryInterface(this); this.queryInterface = new QueryInterface(this);
......
...@@ -7,5 +7,4 @@ const noop = () => {}; ...@@ -7,5 +7,4 @@ const noop = () => {};
exports.noRawAttributes = deprecate(noop, 'Use sequelize.fn / sequelize.literal to construct attributes', 'SEQUELIZE0001'); exports.noRawAttributes = deprecate(noop, 'Use sequelize.fn / sequelize.literal to construct attributes', 'SEQUELIZE0001');
exports.noTrueLogging = deprecate(noop, 'The logging-option should be either a function or false. Default: console.log', 'SEQUELIZE0002'); exports.noTrueLogging = deprecate(noop, 'The logging-option should be either a function or false. Default: console.log', 'SEQUELIZE0002');
exports.noStringOperators = deprecate(noop, 'String based operators are deprecated. Please use Symbol based operators for better security, read more at http://docs.sequelizejs.com/manual/querying.html#operators', 'SEQUELIZE0003'); exports.noStringOperators = deprecate(noop, 'String based operators are deprecated. Please use Symbol based operators for better security, read more at http://docs.sequelizejs.com/manual/querying.html#operators', 'SEQUELIZE0003');
exports.noBoolOperatorAliases = deprecate(noop, 'A boolean value was passed to options.operatorsAliases. This is a no-op with v5 and should be removed.', 'SEQUELIZE0004');
exports.noDoubleNestedGroup = deprecate(noop, 'Passing a double nested nested array to `group` is unsupported and will be removed in v6.', 'SEQUELIZE0005'); exports.noDoubleNestedGroup = deprecate(noop, 'Passing a double nested nested array to `group` is unsupported and will be removed in v6.', 'SEQUELIZE0005');
...@@ -37,15 +37,6 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => { ...@@ -37,15 +37,6 @@ describe(Support.getTestDialectTeaser('Sequelize'), () => {
expect(sequelize.config.host).to.equal('127.0.0.1'); expect(sequelize.config.host).to.equal('127.0.0.1');
}); });
it('should set operators aliases on dialect QueryGenerator', () => {
const operatorsAliases = { fake: true };
const sequelize = Support.createSequelizeInstance({ operatorsAliases });
expect(sequelize).to.have.property('dialect');
expect(sequelize.dialect).to.have.property('QueryGenerator');
expect(sequelize.dialect.QueryGenerator).to.have.property('OperatorsAliasMap');
expect(sequelize.dialect.QueryGenerator.OperatorsAliasMap).to.be.eql(operatorsAliases);
});
if (dialect === 'sqlite') { if (dialect === 'sqlite') {
it('should work with connection strings (1)', () => { it('should work with connection strings (1)', () => {
......
...@@ -43,50 +43,6 @@ describe('QueryGenerator', () => { ...@@ -43,50 +43,6 @@ describe('QueryGenerator', () => {
.to.throw('Invalid value { \'$in\': [ 4 ] }'); .to.throw('Invalid value { \'$in\': [ 4 ] }');
}); });
it('should parse set aliases strings as operators', function() {
const QG = getAbstractQueryGenerator(this.sequelize),
aliases = {
OR: Op.or,
'!': Op.not,
'^^': Op.gt
};
QG.setOperatorsAliases(aliases);
QG.whereItemQuery('OR', [{ test: { '^^': 5 } }, { test: { '!': 3 } }, { test: { [Op.in]: [4] } }])
.should.be.equal('(test > 5 OR test != 3 OR test IN (4))');
QG.whereItemQuery(Op.and, [{ test: { [Op.between]: [2, 5] } }, { test: { '!': 3 } }, { test: { '^^': 4 } }])
.should.be.equal('(test BETWEEN 2 AND 5 AND test != 3 AND test > 4)');
expect(() => QG.whereItemQuery('OR', [{ test: { '^^': 5 } }, { test: { $not: 3 } }, { test: { [Op.in]: [4] } }]))
.to.throw('Invalid value { \'$not\': 3 }');
expect(() => QG.whereItemQuery('OR', [{ test: { $gt: 5 } }, { test: { '!': 3 } }, { test: { [Op.in]: [4] } }]))
.to.throw('Invalid value { \'$gt\': 5 }');
expect(() => QG.whereItemQuery('$or', [{ test: 5 }, { test: 3 }]))
.to.throw('Invalid value { test: 5 }');
expect(() => QG.whereItemQuery('$and', [{ test: 5 }, { test: 3 }]))
.to.throw('Invalid value { test: 5 }');
expect(() => QG.whereItemQuery('test', { $gt: 5 }))
.to.throw('Invalid value { \'$gt\': 5 }');
expect(() => QG.whereItemQuery('test', { $between: [2, 5] }))
.to.throw('Invalid value { \'$between\': [ 2, 5 ] }');
expect(() => QG.whereItemQuery('test', { $ne: 3 }))
.to.throw('Invalid value { \'$ne\': 3 }');
expect(() => QG.whereItemQuery('test', { $not: 3 }))
.to.throw('Invalid value { \'$not\': 3 }');
expect(() => QG.whereItemQuery('test', { $in: [4] }))
.to.throw('Invalid value { \'$in\': [ 4 ] }');
});
it('should correctly parse sequelize.where with .fn as logic', function() { it('should correctly parse sequelize.where with .fn as logic', function() {
const QG = getAbstractQueryGenerator(this.sequelize); const QG = getAbstractQueryGenerator(this.sequelize);
QG.handleSequelizeMethod(this.sequelize.where(this.sequelize.col('foo'), 'LIKE', this.sequelize.col('bar'))) QG.handleSequelizeMethod(this.sequelize.where(this.sequelize.col('foo'), 'LIKE', this.sequelize.col('bar')))
......
...@@ -120,13 +120,6 @@ export interface ReplicationOptions { ...@@ -120,13 +120,6 @@ export interface ReplicationOptions {
} }
/** /**
* Used to map operators to their Symbol representations
*/
export interface OperatorsAliases {
[K: string]: symbol;
}
/**
* Final config options generated by sequelize. * Final config options generated by sequelize.
*/ */
export interface Config { export interface Config {
...@@ -324,15 +317,6 @@ export interface Options extends Logging { ...@@ -324,15 +317,6 @@ export interface Options extends Logging {
typeValidation?: boolean; typeValidation?: boolean;
/** /**
* Sets available operator aliases. See (http://docs.sequelizejs.com/manual/tutorial/querying.html#operators)
* for more information. Set to false to disable operator aliases completely (recommended)
*
* @default all aliases
*/
operatorsAliases?: OperatorsAliases;
/**
* The PostgreSQL `standard_conforming_strings` session parameter. Set to `false` to not set the option. * The PostgreSQL `standard_conforming_strings` session parameter. Set to `false` to not set the option.
* WARNING: Setting this to false may expose vulnerabilities and is not recommended! * WARNING: Setting this to false may expose vulnerabilities and is not recommended!
* *
......
Markdown is supported
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!