Move filter parsing into separate module
This commit is contained in:
parent
9b8244e568
commit
22b04f3a94
|
@ -3,75 +3,22 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var Filter = require('./filter');
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
var Filter = require('./filter');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function AndFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (!options.filters || !Array.isArray(options.filters))
|
||||
throw new TypeError('options.filters ([Filter]) required');
|
||||
this.filters = options.filters.slice();
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.type = Protocol.FILTER_AND;
|
||||
Filter.call(this, options);
|
||||
|
||||
if (!this.filters)
|
||||
this.filters = [];
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'And',
|
||||
filters: self.filters || []
|
||||
};
|
||||
});
|
||||
parents.AndFilter.call(this, options);
|
||||
}
|
||||
util.inherits(AndFilter, Filter);
|
||||
util.inherits(AndFilter, parents.AndFilter);
|
||||
Filter.mixin(AndFilter);
|
||||
module.exports = AndFilter;
|
||||
|
||||
|
||||
AndFilter.prototype.toString = function () {
|
||||
var str = '(&';
|
||||
this.filters.forEach(function (f) {
|
||||
str += f.toString();
|
||||
});
|
||||
str += ')';
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
AndFilter.prototype.matches = function (target) {
|
||||
if (typeof (target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var matches = this.filters.length ? true : false;
|
||||
|
||||
for (var i = 0; i < this.filters.length; i++) {
|
||||
if (!this.filters[i].matches(target))
|
||||
return false;
|
||||
}
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
|
||||
AndFilter.prototype.addFilter = function (filter) {
|
||||
if (!filter || typeof (filter) !== 'object')
|
||||
throw new TypeError('filter (object) required');
|
||||
|
||||
this.filters.push(filter);
|
||||
};
|
||||
|
||||
|
||||
AndFilter.prototype._toBer = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
|
|
|
@ -3,67 +3,22 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var escape = require('./escape').escape;
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function ApproximateFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (!options.attribute || typeof (options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
if (!options.value || typeof (options.value) !== 'string')
|
||||
throw new TypeError('options.value (string) required');
|
||||
this.attribute = options.attribute;
|
||||
this.value = options.value;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
options.type = Protocol.FILTER_APPROX;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'ApproximateMatch',
|
||||
attribute: self.attribute || undefined,
|
||||
value: self.value || undefined
|
||||
};
|
||||
});
|
||||
parents.ApproximateFilter.call(this, options);
|
||||
}
|
||||
util.inherits(ApproximateFilter, Filter);
|
||||
util.inherits(ApproximateFilter, parents.ApproximateFilter);
|
||||
Filter.mixin(ApproximateFilter);
|
||||
module.exports = ApproximateFilter;
|
||||
|
||||
|
||||
ApproximateFilter.prototype.toString = function () {
|
||||
return '(' + escape(this.attribute) + '~=' + escape(this.value) + ')';
|
||||
};
|
||||
|
||||
|
||||
ApproximateFilter.prototype.matches = function (target) {
|
||||
if (typeof (target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var matches = false;
|
||||
var tv = Filter.get_attr_caseless(target, this.attribute);
|
||||
|
||||
if (tv !== null) {
|
||||
if (Array.isArray(tv)) {
|
||||
matches = (tv.indexOf(this.value) != -1);
|
||||
} else {
|
||||
matches = (this.value === tv);
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
};
|
||||
|
||||
|
||||
ApproximateFilter.prototype.parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
|
|
|
@ -4,87 +4,22 @@ var assert = require('assert');
|
|||
var util = require('util');
|
||||
|
||||
var ASN1 = require('asn1').Ber;
|
||||
|
||||
var escape = require('./escape').escape;
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function EqualityFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (!options.attribute || typeof (options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
if (!options.value)
|
||||
throw new TypeError('options.value required');
|
||||
} else {
|
||||
this.raw = new Buffer(0);
|
||||
options = {};
|
||||
}
|
||||
options.type = Protocol.FILTER_EQUALITY;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('value', function () {
|
||||
return self.raw.toString();
|
||||
});
|
||||
this.__defineSetter__('value', function (data) {
|
||||
if (typeof (data) === 'string') {
|
||||
self.raw = new Buffer(data);
|
||||
} else if (Buffer.isBuffer(data)) {
|
||||
self.raw = new Buffer(data.length);
|
||||
data.copy(self.raw);
|
||||
} else {
|
||||
throw new TypeError('value (string|buffer) required');
|
||||
}
|
||||
});
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'EqualityMatch',
|
||||
attribute: self.attribute || undefined,
|
||||
value: self.value || undefined
|
||||
};
|
||||
});
|
||||
if (options.attribute !== undefined && options.value !== undefined) {
|
||||
this.attribute = options.attribute;
|
||||
this.value = options.value;
|
||||
}
|
||||
parents.EqualityFilter.call(this, options);
|
||||
}
|
||||
util.inherits(EqualityFilter, Filter);
|
||||
util.inherits(EqualityFilter, parents.EqualityFilter);
|
||||
Filter.mixin(EqualityFilter);
|
||||
module.exports = EqualityFilter;
|
||||
|
||||
|
||||
EqualityFilter.prototype.toString = function () {
|
||||
return '(' + escape(this.attribute) + '=' + escape(this.value) + ')';
|
||||
};
|
||||
|
||||
|
||||
EqualityFilter.prototype.matches = function (target) {
|
||||
if (typeof (target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var self = this;
|
||||
var tv = Filter.get_attr_caseless(target, this.attribute);
|
||||
|
||||
if (tv !== null) {
|
||||
var value = this.value;
|
||||
return Filter.multi_test(
|
||||
function (v) {
|
||||
if (self.attribute === 'objectclass')
|
||||
v = v.toLowerCase();
|
||||
return value === v;
|
||||
},
|
||||
tv);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
EqualityFilter.prototype.parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
|
|
|
@ -3,132 +3,27 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
var SubstringFilter = require('./substr_filter');
|
||||
|
||||
|
||||
// THIS IS A STUB!
|
||||
//
|
||||
// ldapjs does not support server side extensible matching.
|
||||
// This class exists only for the client to send them.
|
||||
|
||||
///--- API
|
||||
|
||||
function ExtensibleFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (options.rule && typeof (options.rule) !== 'string')
|
||||
throw new TypeError('options.rule must be a string');
|
||||
if (options.matchType && typeof (options.matchType) !== 'string')
|
||||
throw new TypeError('options.type must be a string');
|
||||
if (options.value && typeof (options.value) !== 'string')
|
||||
throw new TypeError('options.value (string) required');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.any = options.any || null;
|
||||
this.attribute = options.matchType || null;
|
||||
this['final'] = options['final'] || null;
|
||||
this.initial = options.initial || null;
|
||||
this.matchType = options.matchType || null;
|
||||
this.rule = options.rule || null;
|
||||
this.value = options.value || '';
|
||||
this.dnAttributes = options.dnAttributes || false;
|
||||
options.type = Protocol.FILTER_EXT;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'ExtensibleMatch',
|
||||
matchRule: self.rule,
|
||||
matchType: self.matchType,
|
||||
matchValue: self.value,
|
||||
dnAttributes: self.dnAttributes
|
||||
};
|
||||
});
|
||||
this.__defineGetter__('matchingRule', function () {
|
||||
return self.rule;
|
||||
});
|
||||
this.__defineGetter__('matchValue', function () {
|
||||
return self.value;
|
||||
});
|
||||
parents.ExtensibleFilter.call(this, options);
|
||||
}
|
||||
util.inherits(ExtensibleFilter, Filter);
|
||||
util.inherits(ExtensibleFilter, parents.ExtensibleFilter);
|
||||
Filter.mixin(ExtensibleFilter);
|
||||
module.exports = ExtensibleFilter;
|
||||
|
||||
|
||||
ExtensibleFilter.prototype.toString = function () {
|
||||
var str = '(';
|
||||
|
||||
if (this.matchType)
|
||||
str += this.matchType;
|
||||
|
||||
str += ':';
|
||||
|
||||
if (this.dnAttributes)
|
||||
str += 'dn:';
|
||||
|
||||
if (this.rule)
|
||||
str += this.rule + ':';
|
||||
|
||||
return (str + '=' + this.value + ')');
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* THIS IS A STUB!
|
||||
*
|
||||
* ldapjs does not support server side extensible matching. This class exists
|
||||
* only for the client to send them.
|
||||
*
|
||||
* @param {Object} target the target object.
|
||||
* @return {Boolean} false always.
|
||||
*/
|
||||
ExtensibleFilter.prototype.matches = function (target) {
|
||||
if (typeof (target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
if (this.dnAttribute)
|
||||
throw new Error('ExtensibleMatch dnAttributes not supported');
|
||||
|
||||
if (!this.value)
|
||||
return false;
|
||||
|
||||
var self = this;
|
||||
var tv = Filter.get_attr_caseless(target, this.matchType);
|
||||
|
||||
if (this.matchType && tv !== null) {
|
||||
|
||||
if (self.rule === '2.5.13.4' || self.rule === 'caseIgnoreSubstringsMatch') {
|
||||
var f = {
|
||||
attribute: self.matchType,
|
||||
initial: self.initial ? self.initial.toLowerCase() : undefined,
|
||||
any: self.any ? self.any.map(function (a) {
|
||||
return a.toLowerCase();
|
||||
}) : undefined,
|
||||
'final': self['final'] ? self['final'].toLowerCase() : undefined
|
||||
};
|
||||
}
|
||||
|
||||
return Filter.multi_test(function (v) {
|
||||
if (self.rule === '2.5.13.2' || self.rule === 'caseIgnoreMatch') {
|
||||
return self.value.toLowerCase() === v.toLowerCase();
|
||||
} else if (self.rule === '2.5.13.4' ||
|
||||
self.rule === 'caseIgnoreSubstringsMatch') {
|
||||
|
||||
var t = {};
|
||||
t[self.matchType] = v.toLowerCase();
|
||||
|
||||
return SubstringFilter.prototype.matches.call(f, t);
|
||||
}
|
||||
|
||||
return self.value === v;
|
||||
}, tv);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
ExtensibleFilter.prototype.parse = function (ber) {
|
||||
var end = ber.offset + ber.length;
|
||||
while (ber.offset < end) {
|
||||
|
|
|
@ -4,106 +4,55 @@ var assert = require('assert');
|
|||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var BerWriter = asn1.BerWriter;
|
||||
|
||||
var TYPES = {
|
||||
'and': Protocol.FILTER_AND,
|
||||
'or': Protocol.FILTER_OR,
|
||||
'not': Protocol.FILTER_NOT,
|
||||
'equal': Protocol.FILTER_EQUALITY,
|
||||
'substring': Protocol.FILTER_SUBSTRINGS,
|
||||
'ge': Protocol.FILTER_GE,
|
||||
'le': Protocol.FILTER_LE,
|
||||
'present': Protocol.FILTER_PRESENT,
|
||||
'approx': Protocol.FILTER_APPROX,
|
||||
'ext': Protocol.FILTER_EXT
|
||||
};
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function Filter(options) {
|
||||
if (!options || typeof (options) !== 'object')
|
||||
throw new TypeError('options (object) required');
|
||||
if (typeof (options.type) !== 'number')
|
||||
throw new TypeError('options.type (number) required');
|
||||
|
||||
this._type = options.type;
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('type', function () {
|
||||
switch (self._type) {
|
||||
case Protocol.FILTER_AND: return 'and';
|
||||
case Protocol.FILTER_OR: return 'or';
|
||||
case Protocol.FILTER_NOT: return 'not';
|
||||
case Protocol.FILTER_EQUALITY: return 'equal';
|
||||
case Protocol.FILTER_SUBSTRINGS: return 'substring';
|
||||
case Protocol.FILTER_GE: return 'ge';
|
||||
case Protocol.FILTER_LE: return 'le';
|
||||
case Protocol.FILTER_PRESENT: return 'present';
|
||||
case Protocol.FILTER_APPROX: return 'approx';
|
||||
case Protocol.FILTER_EXT: return 'ext';
|
||||
default:
|
||||
throw new Error('0x' + self._type.toString(16) +
|
||||
' is an invalid search filter');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Filter.isFilter = function isFilter(filter) {
|
||||
function isFilter(filter) {
|
||||
if (!filter || typeof (filter) !== 'object') {
|
||||
return false;
|
||||
}
|
||||
// Do our best to duck-type it
|
||||
if ((filter instanceof Filter) || (
|
||||
typeof (filter.toBer) === 'functin' &&
|
||||
if (typeof (filter.toBer) === 'function' &&
|
||||
typeof (filter.matches) === 'function' &&
|
||||
typeof (filter._type) === 'number')) {
|
||||
TYPES[filter.type] !== undefined) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
module.exports = Filter;
|
||||
|
||||
|
||||
Filter.prototype.toBer = function (ber) {
|
||||
if (!ber || !(ber instanceof BerWriter))
|
||||
throw new TypeError('ber (BerWriter) required');
|
||||
|
||||
ber.startSequence(this._type);
|
||||
ber = this._toBer(ber);
|
||||
ber.endSequence();
|
||||
return ber;
|
||||
};
|
||||
|
||||
|
||||
// Test a rule against one or more values.
|
||||
Filter.multi_test = function (rule, value) {
|
||||
if (Array.isArray(value)) {
|
||||
var response = false;
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
if (rule(value[i])) {
|
||||
response = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return response;
|
||||
} else {
|
||||
return rule(value);
|
||||
}
|
||||
};
|
||||
|
||||
// Search object for attribute, insensitive to case
|
||||
Filter.get_attr_caseless = function (target, attr) {
|
||||
// Check for exact case match first
|
||||
if (target.hasOwnProperty(attr)) {
|
||||
return target[attr];
|
||||
}
|
||||
// Perform case-insensitive enumeration after that
|
||||
var lower = attr.toLowerCase();
|
||||
var result = null;
|
||||
Object.getOwnPropertyNames(target).some(function (item) {
|
||||
if (item.toLowerCase() == lower) {
|
||||
result = target[item];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
function mixin(target) {
|
||||
target.prototype.toBer = function toBer(ber) {
|
||||
if (!ber || !(ber instanceof BerWriter))
|
||||
throw new TypeError('ber (BerWriter) required');
|
||||
|
||||
ber.startSequence(TYPES[this.type]);
|
||||
ber = this._toBer(ber);
|
||||
ber.endSequence();
|
||||
return ber;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
isFilter: isFilter,
|
||||
mixin: mixin
|
||||
};
|
||||
|
|
|
@ -3,66 +3,21 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var escape = require('./escape').escape;
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function GreaterThanEqualsFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (!options.attribute || typeof (options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
if (!options.value || typeof (options.value) !== 'string')
|
||||
throw new TypeError('options.value (string) required');
|
||||
this.attribute = options.attribute;
|
||||
this.value = options.value;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.type = Protocol.FILTER_GE;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'GreaterThanEqualsMatch',
|
||||
attribute: self.attribute || undefined,
|
||||
value: self.value || undefined
|
||||
};
|
||||
});
|
||||
parents.GreaterThanEqualsFilter.call(this, options);
|
||||
}
|
||||
util.inherits(GreaterThanEqualsFilter, Filter);
|
||||
util.inherits(GreaterThanEqualsFilter, parents.GreaterThanEqualsFilter);
|
||||
Filter.mixin(GreaterThanEqualsFilter);
|
||||
module.exports = GreaterThanEqualsFilter;
|
||||
|
||||
|
||||
GreaterThanEqualsFilter.prototype.toString = function () {
|
||||
return '(' + escape(this.attribute) + '>=' + escape(this.value) + ')';
|
||||
};
|
||||
|
||||
|
||||
GreaterThanEqualsFilter.prototype.matches = function (target) {
|
||||
if (typeof (target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var tv = Filter.get_attr_caseless(target, this.attribute);
|
||||
|
||||
if (tv !== null) {
|
||||
var value = this.value;
|
||||
return Filter.multi_test(
|
||||
function (v) { return value <= v; },
|
||||
tv);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
GreaterThanEqualsFilter.prototype.parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
|
|
|
@ -4,6 +4,8 @@ var assert = require('assert');
|
|||
|
||||
var asn1 = require('asn1');
|
||||
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
@ -25,400 +27,8 @@ var SubstringFilter = require('./substr_filter');
|
|||
var BerReader = asn1.BerReader;
|
||||
|
||||
|
||||
|
||||
///--- Internal Parsers
|
||||
|
||||
// expression parsing
|
||||
// returns the index of the closing parenthesis matching the open paren
|
||||
// specified by openParenIndex
|
||||
function matchParens(str, openParenIndex) {
|
||||
var stack = [];
|
||||
var esc = false;
|
||||
for (var i = openParenIndex || 0; i < str.length; i++) {
|
||||
var c = str[i];
|
||||
|
||||
if (c === '\\') {
|
||||
if (!esc)
|
||||
esc = true;
|
||||
continue;
|
||||
} else if (c === '(' && !esc) {
|
||||
stack.push(1);
|
||||
} else if (c === ')' && !esc) {
|
||||
stack.pop();
|
||||
if (stack.length === 0)
|
||||
return i;
|
||||
}
|
||||
|
||||
esc = false;
|
||||
}
|
||||
|
||||
var ndx = str.length - 1;
|
||||
if (str.charAt(ndx) !== ')')
|
||||
throw new Error(str + ' has unbalanced parentheses');
|
||||
|
||||
return ndx;
|
||||
}
|
||||
|
||||
|
||||
function parse_substr(tree) {
|
||||
// Effectively a hand-rolled .shift() to support \* sequences
|
||||
var clean = true;
|
||||
var esc = false;
|
||||
var obj = {};
|
||||
var split = [];
|
||||
var substrNdx = 0;
|
||||
|
||||
split[substrNdx] = '';
|
||||
|
||||
for (var i = 0; i < tree.value.length; i++) {
|
||||
var c = tree.value[i];
|
||||
if (esc) {
|
||||
split[substrNdx] += c;
|
||||
esc = false;
|
||||
} else if (c === '*') {
|
||||
split[++substrNdx] = '';
|
||||
} else if (c === '\\') {
|
||||
esc = true;
|
||||
} else {
|
||||
split[substrNdx] += c;
|
||||
}
|
||||
}
|
||||
|
||||
if (split.length > 1) {
|
||||
obj.tag = 'substrings';
|
||||
clean = true;
|
||||
|
||||
// if the value string doesn't start with a * then theres no initial
|
||||
// value else split will have an empty string in its first array
|
||||
// index...
|
||||
// we need to remove that empty string
|
||||
if (tree.value.indexOf('*') !== 0) {
|
||||
obj.initial = split.shift();
|
||||
} else {
|
||||
split.shift();
|
||||
}
|
||||
|
||||
// if the value string doesn't end with a * then theres no final
|
||||
// value also same split stuff as the initial stuff above
|
||||
if (tree.value.lastIndexOf('*') !== tree.value.length - 1) {
|
||||
obj['final'] = split.pop();
|
||||
} else {
|
||||
split.pop();
|
||||
}
|
||||
obj.any = split;
|
||||
} else {
|
||||
obj.value = split[0]; // pick up the cleaned version
|
||||
}
|
||||
|
||||
obj.clean = clean;
|
||||
obj.esc = esc;
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
// recursive function that builds a filter tree from a string expression
|
||||
// the filter tree is an intermediary step between the incoming expression and
|
||||
// the outgoing Filter Class structure.
|
||||
function _buildFilterTree(expr) {
|
||||
var c;
|
||||
var child;
|
||||
var clean = false;
|
||||
var endParen;
|
||||
var esc = false;
|
||||
var i = 0;
|
||||
var obj;
|
||||
var tree = {};
|
||||
var split;
|
||||
var val = '';
|
||||
|
||||
if (expr.length === 0)
|
||||
return tree;
|
||||
|
||||
// Chop the parens (the call to matchParens below gets rid of the trailer)
|
||||
if (expr.charAt(0) == '(')
|
||||
expr = expr.substring(1, expr.length - 1);
|
||||
|
||||
//store prefix operator
|
||||
if (expr.charAt(0) === '&') {
|
||||
tree.op = 'and';
|
||||
expr = expr.substring(1);
|
||||
} else if (expr.charAt(0) === '|') {
|
||||
tree.op = 'or';
|
||||
expr = expr.substring(1);
|
||||
} else if (expr.charAt(0) === '!') {
|
||||
tree.op = 'not';
|
||||
expr = expr.substring(1);
|
||||
} else {
|
||||
tree.op = 'expr';
|
||||
}
|
||||
|
||||
if (tree.op != 'expr') {
|
||||
tree.children = [];
|
||||
|
||||
// logical operators are k-ary, so we go until our expression string runs
|
||||
// out (at least for this recursion level)
|
||||
while (expr.length !== 0) {
|
||||
endParen = matchParens(expr);
|
||||
|
||||
if (endParen == expr.length - 1) {
|
||||
tree.children[i] = _buildFilterTree(expr);
|
||||
expr = '';
|
||||
} else {
|
||||
child = expr.slice(0, endParen + 1);
|
||||
expr = expr.substring(endParen + 1);
|
||||
tree.children[i] = _buildFilterTree(child);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
//else its some sort of non-logical expression, parse and return as such
|
||||
var operatorStr = '';
|
||||
tree.name = '';
|
||||
tree.value = '';
|
||||
|
||||
|
||||
// This parses and enforces filter syntax, which is an AttributeDescription
|
||||
// plus a filter operator, followed by (for ldapjs), anything. Note
|
||||
// that ldapjs additionally allows the '_' character in the AD, as many
|
||||
// users rely on it, even though it's non-standard
|
||||
//
|
||||
// From 4.1.5 of RFC251
|
||||
//
|
||||
// AttributeDescription ::= LDAPString
|
||||
//
|
||||
// A value of AttributeDescription is based on the following BNF:
|
||||
//
|
||||
// <AttributeDescription> ::= <AttributeType> [ ";" <options> ]
|
||||
//
|
||||
// <options> ::= <option> | <option> ";" <options>
|
||||
//
|
||||
// <option> ::= <opt-char> <opt-char>*
|
||||
//
|
||||
// <opt-char> ::= ASCII-equivalent letters, numbers and hyphen
|
||||
//
|
||||
// Examples of valid AttributeDescription:
|
||||
//
|
||||
// cn
|
||||
// userCertificate;binary
|
||||
|
||||
/* JSSTYLED */
|
||||
if (!/[a-zA-Z0-9;_\-]+[~><:]?=.+/.test(expr))
|
||||
throw new Error(expr + ' is invalid');
|
||||
|
||||
if (expr.indexOf('~=') !== -1) {
|
||||
operatorStr = '~=';
|
||||
tree.tag = 'approxMatch';
|
||||
} else if (expr.indexOf('>=') !== -1) {
|
||||
operatorStr = '>=';
|
||||
tree.tag = 'greaterOrEqual';
|
||||
} else if (expr.indexOf('<=') !== -1) {
|
||||
operatorStr = '<=';
|
||||
tree.tag = 'lessOrEqual';
|
||||
} else if (expr.indexOf(':=') !== -1) {
|
||||
operatorStr = ':=';
|
||||
tree.tag = 'extensibleMatch';
|
||||
} else if (expr.indexOf('=') !== -1) {
|
||||
operatorStr = '=';
|
||||
tree.tag = 'equalityMatch';
|
||||
} else {
|
||||
// tree.tag = 'present';
|
||||
throw new Error('invalid filter syntax');
|
||||
}
|
||||
|
||||
if (operatorStr === '') {
|
||||
tree.name = expr;
|
||||
} else {
|
||||
// pull out lhs and rhs of equality operator
|
||||
var splitAry = expr.split(operatorStr);
|
||||
tree.name = splitAry.shift();
|
||||
tree.value = splitAry.join(operatorStr);
|
||||
|
||||
// substrings fall into the equality bin in the
|
||||
// switch above so we need more processing here
|
||||
if (tree.tag === 'equalityMatch') {
|
||||
if (tree.value === '*') {
|
||||
tree.tag = 'present';
|
||||
} else {
|
||||
obj = parse_substr(tree);
|
||||
tree.initial = obj.initial;
|
||||
tree.any = obj.any;
|
||||
tree['final'] = obj['final'];
|
||||
tree.tag = obj.tag || tree.tag;
|
||||
tree.value = obj.value;
|
||||
esc = obj.esc;
|
||||
clean = obj.clean;
|
||||
}
|
||||
} else if (tree.tag == 'extensibleMatch') {
|
||||
split = tree.name.split(':');
|
||||
tree.extensible = {
|
||||
matchType: split[0],
|
||||
value: tree.value
|
||||
};
|
||||
|
||||
switch (split.length) {
|
||||
case 1:
|
||||
break;
|
||||
case 2:
|
||||
if (split[1].toLowerCase() === 'dn') {
|
||||
tree.extensible.dnAttributes = true;
|
||||
} else {
|
||||
tree.extensible.rule = split[1];
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
tree.extensible.dnAttributes = true;
|
||||
tree.extensible.rule = split[2];
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid extensible filter');
|
||||
}
|
||||
|
||||
switch (tree.extensible.rule) {
|
||||
case '2.5.13.4':
|
||||
case 'caseIgnoreSubstringsMatch':
|
||||
tree.extensible.attribute = tree.extensible.matchType;
|
||||
obj = parse_substr(tree);
|
||||
tree.extensible.initial = obj.initial;
|
||||
tree.extensible.any = obj.any;
|
||||
tree.extensible['final'] = obj['final'];
|
||||
tree.value = obj.value;
|
||||
esc = obj.esc;
|
||||
clean = obj.clean;
|
||||
break;
|
||||
|
||||
case '2.5.13.2':
|
||||
case 'caseIgnoreMatch':
|
||||
tree.extensible.attribute = tree.extensible.matchType;
|
||||
break;
|
||||
default:
|
||||
// noop
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup any escape sequences
|
||||
if (!clean) {
|
||||
|
||||
for (i = 0; i < tree.value.length; i++) {
|
||||
c = tree.value[i];
|
||||
if (esc) {
|
||||
val += c;
|
||||
esc = false;
|
||||
} else if (c === '\\') {
|
||||
esc = true;
|
||||
} else {
|
||||
val += c;
|
||||
}
|
||||
}
|
||||
tree.value = val;
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
function serializeTree(tree, filter) {
|
||||
if (tree === undefined || tree.length === 0)
|
||||
return;
|
||||
|
||||
// if the current tree object is not an expression then its a logical
|
||||
// operator (ie an internal node in the tree)
|
||||
var current = null;
|
||||
if (tree.op !== 'expr') {
|
||||
switch (tree.op) {
|
||||
case 'and':
|
||||
current = new AndFilter();
|
||||
break;
|
||||
case 'or':
|
||||
current = new OrFilter();
|
||||
break;
|
||||
case 'not':
|
||||
current = new NotFilter();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
filter.addFilter(current || filter);
|
||||
if (current || tree.children.length) {
|
||||
tree.children.forEach(function (child) {
|
||||
serializeTree(child, current);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
// else its a leaf node in the tree, and represents some type of
|
||||
// non-logical expression
|
||||
var tmp;
|
||||
|
||||
// convert the tag name to a filter class type
|
||||
switch (tree.tag) {
|
||||
case 'approxMatch':
|
||||
tmp = new ApproximateFilter({
|
||||
attribute: tree.name,
|
||||
value: tree.value
|
||||
});
|
||||
break;
|
||||
case 'extensibleMatch':
|
||||
tmp = new ExtensibleFilter(tree.extensible);
|
||||
break;
|
||||
case 'greaterOrEqual':
|
||||
tmp = new GreaterThanEqualsFilter({
|
||||
attribute: tree.name,
|
||||
value: tree.value
|
||||
});
|
||||
break;
|
||||
case 'lessOrEqual':
|
||||
tmp = new LessThanEqualsFilter({
|
||||
attribute: tree.name,
|
||||
value: tree.value
|
||||
});
|
||||
break;
|
||||
case 'equalityMatch':
|
||||
tmp = new EqualityFilter({
|
||||
attribute: tree.name,
|
||||
value: tree.value
|
||||
});
|
||||
break;
|
||||
case 'substrings':
|
||||
tmp = new SubstringFilter({
|
||||
attribute: tree.name,
|
||||
initial: tree.initial,
|
||||
any: tree.any,
|
||||
'final': tree['final']
|
||||
});
|
||||
break;
|
||||
case 'present':
|
||||
tmp = new PresenceFilter({
|
||||
attribute: tree.name
|
||||
});
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (tmp)
|
||||
filter.addFilter(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _parseString(str) {
|
||||
assert.ok(str);
|
||||
|
||||
// create a blank object to pass into treeToObjs
|
||||
// since its recursive we have to prime it ourselves.
|
||||
// this gets stripped off before the filter structure is returned
|
||||
// at the bottom of this function.
|
||||
var filterObj = new AndFilter({
|
||||
filters: []
|
||||
});
|
||||
|
||||
serializeTree(_buildFilterTree(str), filterObj);
|
||||
return filterObj.filters[0];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* A filter looks like this coming in:
|
||||
* Filter ::= CHOICE {
|
||||
|
@ -528,11 +138,51 @@ function _parse(ber) {
|
|||
}
|
||||
|
||||
|
||||
function cloneFilter(input) {
|
||||
var child;
|
||||
if (input.type === 'and' || input.type === 'or') {
|
||||
child = input.filters.map(cloneFilter);
|
||||
} else if (input.type === 'not') {
|
||||
child = cloneFilter(input.filter);
|
||||
}
|
||||
switch (input.type) {
|
||||
case 'and':
|
||||
return new AndFilter({filters: child});
|
||||
case 'or':
|
||||
return new OrFilter({filters: child});
|
||||
case 'not':
|
||||
return new NotFilter({filter: child});
|
||||
case 'equal':
|
||||
return new EqualityFilter(input);
|
||||
case 'substring':
|
||||
return new SubstringFilter(input);
|
||||
case 'ge':
|
||||
return new GreaterThanEqualsFilter(input);
|
||||
case 'le':
|
||||
return new LessThanEqualsFilter(input);
|
||||
case 'present':
|
||||
return new PresenceFilter(input);
|
||||
case 'approx':
|
||||
return new ApproximateFilter(input);
|
||||
case 'ext':
|
||||
return new ExtensibleFilter(input);
|
||||
default:
|
||||
throw new Error('invalid filter type:' + input.type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function parseString(str) {
|
||||
var generic = parents.parse(str);
|
||||
// The filter object(s) return from ldap-filter.parse lack the toBer/parse
|
||||
// decoration that native ldapjs filter possess. cloneFilter adds that back.
|
||||
return cloneFilter(generic);
|
||||
}
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
module.exports = {
|
||||
|
||||
parse: function (ber) {
|
||||
if (!ber || !(ber instanceof BerReader))
|
||||
throw new TypeError('ber (BerReader) required');
|
||||
|
@ -540,12 +190,7 @@ module.exports = {
|
|||
return _parse(ber);
|
||||
},
|
||||
|
||||
parseString: function (filter) {
|
||||
if (!filter || typeof (filter) !== 'string')
|
||||
throw new TypeError('filter (string) required');
|
||||
|
||||
return _parseString(filter);
|
||||
},
|
||||
parseString: parseString,
|
||||
|
||||
isFilter: Filter.isFilter,
|
||||
|
||||
|
@ -558,6 +203,5 @@ module.exports = {
|
|||
NotFilter: NotFilter,
|
||||
OrFilter: OrFilter,
|
||||
PresenceFilter: PresenceFilter,
|
||||
SubstringFilter: SubstringFilter,
|
||||
Filter: Filter
|
||||
SubstringFilter: SubstringFilter
|
||||
};
|
||||
|
|
|
@ -3,66 +3,21 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var escape = require('./escape').escape;
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function LessThanEqualsFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (!options.attribute || typeof (options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
if (!options.value || typeof (options.value) !== 'string')
|
||||
throw new TypeError('options.value (string) required');
|
||||
this.attribute = options.attribute;
|
||||
this.value = options.value;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.type = Protocol.FILTER_LE;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'LessThanEqualsMatch',
|
||||
attribute: self.attribute || undefined,
|
||||
value: self.value || undefined
|
||||
};
|
||||
});
|
||||
parents.LessThanEqualsFilter.call(this, options);
|
||||
}
|
||||
util.inherits(LessThanEqualsFilter, Filter);
|
||||
util.inherits(LessThanEqualsFilter, parents.LessThanEqualsFilter);
|
||||
Filter.mixin(LessThanEqualsFilter);
|
||||
module.exports = LessThanEqualsFilter;
|
||||
|
||||
|
||||
LessThanEqualsFilter.prototype.toString = function () {
|
||||
return '(' + escape(this.attribute) + '<=' + escape(this.value) + ')';
|
||||
};
|
||||
|
||||
|
||||
LessThanEqualsFilter.prototype.matches = function (target) {
|
||||
if (typeof (target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var tv = Filter.get_attr_caseless(target, this.attribute);
|
||||
|
||||
if (tv !== null) {
|
||||
var value = this.value;
|
||||
return Filter.multi_test(
|
||||
function (v) { return value >= v; },
|
||||
tv);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
LessThanEqualsFilter.prototype.parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
|
|
|
@ -3,55 +3,21 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function NotFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (!options.filter || !Filter.isFilter(options.filter))
|
||||
throw new TypeError('options.filter (Filter) required');
|
||||
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
this.filter = options.filter || {};
|
||||
options.type = Protocol.FILTER_NOT;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'Not',
|
||||
filter: self.filter
|
||||
};
|
||||
});
|
||||
parents.NotFilter.call(this, options);
|
||||
}
|
||||
util.inherits(NotFilter, Filter);
|
||||
util.inherits(NotFilter, parents.NotFilter);
|
||||
Filter.mixin(NotFilter);
|
||||
module.exports = NotFilter;
|
||||
|
||||
|
||||
NotFilter.prototype.addFilter = function (f) {
|
||||
if (!Filter.isFilter(f))
|
||||
throw new TypeError('filter (Filter) required');
|
||||
this.filter = f;
|
||||
};
|
||||
|
||||
NotFilter.prototype.toString = function () {
|
||||
return '(!' + this.filter.toString() + ')';
|
||||
};
|
||||
|
||||
|
||||
NotFilter.prototype.matches = function (target) {
|
||||
return !this.filter.matches(target);
|
||||
};
|
||||
|
||||
|
||||
NotFilter.prototype._toBer = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
|
|
|
@ -3,73 +3,21 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function OrFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (!options.filters || !Array.isArray(options.filters))
|
||||
throw new TypeError('options.filters ([Filter]) required');
|
||||
this.filters = options.filters.slice();
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.type = Protocol.FILTER_OR;
|
||||
Filter.call(this, options);
|
||||
|
||||
if (!this.filters)
|
||||
this.filters = [];
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'Or',
|
||||
filters: self.filters || []
|
||||
};
|
||||
});
|
||||
parents.OrFilter.call(this, options);
|
||||
}
|
||||
util.inherits(OrFilter, Filter);
|
||||
util.inherits(OrFilter, parents.OrFilter);
|
||||
Filter.mixin(OrFilter);
|
||||
module.exports = OrFilter;
|
||||
|
||||
|
||||
OrFilter.prototype.toString = function () {
|
||||
var str = '(|';
|
||||
this.filters.forEach(function (f) {
|
||||
str += f.toString();
|
||||
});
|
||||
str += ')';
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
OrFilter.prototype.matches = function (target) {
|
||||
if (typeof (target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
for (var i = 0; i < this.filters.length; i++) {
|
||||
if (this.filters[i].matches(target))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
OrFilter.prototype.addFilter = function (filter) {
|
||||
if (!filter || typeof (filter) !== 'object')
|
||||
throw new TypeError('filter (object) required');
|
||||
|
||||
this.filters.push(filter);
|
||||
};
|
||||
|
||||
|
||||
OrFilter.prototype._toBer = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
|
|
|
@ -3,51 +3,21 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var escape = require('./escape').escape;
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function PresenceFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (!options.attribute || typeof (options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
this.attribute = options.attribute;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
options.type = Protocol.FILTER_PRESENT;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'PresenceMatch',
|
||||
attribute: self.attribute || undefined
|
||||
};
|
||||
});
|
||||
parents.PresenceFilter.call(this, options);
|
||||
}
|
||||
util.inherits(PresenceFilter, Filter);
|
||||
util.inherits(PresenceFilter, parents.PresenceFilter);
|
||||
Filter.mixin(PresenceFilter);
|
||||
module.exports = PresenceFilter;
|
||||
|
||||
|
||||
PresenceFilter.prototype.toString = function () {
|
||||
return '(' + escape(this.attribute) + '=*)';
|
||||
};
|
||||
|
||||
|
||||
PresenceFilter.prototype.matches = function (target) {
|
||||
if (typeof (target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
return (Filter.get_attr_caseless(target, this.attribute) !== null);
|
||||
};
|
||||
|
||||
|
||||
PresenceFilter.prototype.parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
|
|
|
@ -3,107 +3,21 @@
|
|||
var assert = require('assert');
|
||||
var util = require('util');
|
||||
|
||||
var escape = require('./escape').escape;
|
||||
var parents = require('ldap-filter');
|
||||
|
||||
var Filter = require('./filter');
|
||||
|
||||
var Protocol = require('../protocol');
|
||||
|
||||
|
||||
|
||||
///--- API
|
||||
|
||||
function SubstringFilter(options) {
|
||||
if (typeof (options) === 'object') {
|
||||
if (!options.attribute || typeof (options.attribute) !== 'string')
|
||||
throw new TypeError('options.attribute (string) required');
|
||||
this.attribute = options.attribute;
|
||||
this.initial = options.initial ? options.initial : null;
|
||||
this.any = options.any ? options.any.slice(0) : [];
|
||||
this['final'] = options['final'] || null;
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (!this.any)
|
||||
this.any = [];
|
||||
|
||||
options.type = Protocol.FILTER_SUBSTRINGS;
|
||||
Filter.call(this, options);
|
||||
|
||||
var self = this;
|
||||
this.__defineGetter__('json', function () {
|
||||
return {
|
||||
type: 'SubstringMatch',
|
||||
initial: self.initial || undefined,
|
||||
any: self.any || undefined,
|
||||
'final': self['final'] || undefined
|
||||
};
|
||||
});
|
||||
parents.SubstringFilter.call(this, options);
|
||||
}
|
||||
util.inherits(SubstringFilter, Filter);
|
||||
util.inherits(SubstringFilter, parents.SubstringFilter);
|
||||
Filter.mixin(SubstringFilter);
|
||||
module.exports = SubstringFilter;
|
||||
|
||||
|
||||
SubstringFilter.prototype.toString = function () {
|
||||
var str = '(' + escape(this.attribute) + '=';
|
||||
|
||||
if (this.initial)
|
||||
str += escape(this.initial);
|
||||
|
||||
str += '*';
|
||||
|
||||
this.any.forEach(function (s) {
|
||||
str += escape(s) + '*';
|
||||
});
|
||||
|
||||
if (this['final'])
|
||||
str += escape(this['final']);
|
||||
|
||||
str += ')';
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
function escapeRegExp(str) {
|
||||
/* JSSTYLED */
|
||||
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&');
|
||||
}
|
||||
|
||||
SubstringFilter.prototype.matches = function (target) {
|
||||
if (typeof (target) !== 'object')
|
||||
throw new TypeError('target (object) required');
|
||||
|
||||
var tv = Filter.get_attr_caseless(target, this.attribute);
|
||||
|
||||
if (tv !== null) {
|
||||
var re = '';
|
||||
if (this.initial)
|
||||
re += '^' + escapeRegExp(this.initial) + '.*';
|
||||
|
||||
this.any.forEach(function (s) {
|
||||
re += escapeRegExp(s) + '.*';
|
||||
});
|
||||
|
||||
if (this['final'])
|
||||
re += escapeRegExp(this['final']) + '$';
|
||||
|
||||
var matcher = new RegExp(re);
|
||||
var self = this;
|
||||
return Filter.multi_test(
|
||||
function (v) {
|
||||
if (self.attribute === 'objectclass')
|
||||
v = v.toLowerCase();
|
||||
return matcher.test(v);
|
||||
},
|
||||
tv);
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
|
||||
SubstringFilter.prototype.parse = function (ber) {
|
||||
assert.ok(ber);
|
||||
|
||||
|
@ -126,9 +40,9 @@ SubstringFilter.prototype.parse = function (ber) {
|
|||
this.any.push(anyVal);
|
||||
break;
|
||||
case 0x82: // Final
|
||||
this['final'] = ber.readString(tag);
|
||||
this.final = ber.readString(tag);
|
||||
if (this.attribute === 'objectclass')
|
||||
this['final'] = this['final'].toLowerCase();
|
||||
this.final = this.final.toLowerCase();
|
||||
break;
|
||||
default:
|
||||
throw new Error('Invalid substrings filter type: 0x' + tag.toString(16));
|
||||
|
@ -153,8 +67,8 @@ SubstringFilter.prototype._toBer = function (ber) {
|
|||
ber.writeString(s, 0x81);
|
||||
});
|
||||
|
||||
if (this['final'])
|
||||
ber.writeString(this['final'], 0x82);
|
||||
if (this.final)
|
||||
ber.writeString(this.final, 0x82);
|
||||
|
||||
ber.endSequence();
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
"bunyan": "0.23.1",
|
||||
"dashdash": "1.7.0",
|
||||
"backoff": "2.4.0",
|
||||
"ldap-filter": "0.1.1",
|
||||
"once": "1.3.0",
|
||||
"vasync": "1.5.0",
|
||||
"verror": "~1.4.0"
|
||||
|
|
|
@ -59,39 +59,6 @@ test('GH-109 = escape value only in toString()', function (t) {
|
|||
});
|
||||
|
||||
|
||||
test('match true', function (t) {
|
||||
var f = new ApproximateFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('match multiple', function (t) {
|
||||
var f = new ApproximateFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(f.matches({ foo: ['steak', 'bar']}));
|
||||
t.ok(!f.matches({ foo: ['nihhh', 'rabbit']}));
|
||||
t.end();
|
||||
});
|
||||
|
||||
test('match false', function (t) {
|
||||
var f = new ApproximateFilter({
|
||||
attribute: 'foo',
|
||||
value: 'bar'
|
||||
});
|
||||
t.ok(f);
|
||||
t.ok(!f.matches({ foo: 'baz' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse ok', function (t) {
|
||||
var writer = new BerWriter();
|
||||
writer.writeString('foo');
|
||||
|
@ -100,7 +67,6 @@ test('parse ok', function (t) {
|
|||
var f = new ApproximateFilter();
|
||||
t.ok(f);
|
||||
t.ok(f.parse(new BerReader(writer.buffer)));
|
||||
t.ok(f.matches({ foo: 'bar' }));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
|
|
@ -108,26 +108,3 @@ test('parse RFC example 5', function (t) {
|
|||
t.ok(f.dnAttributes);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse caseIgnore', function (t) {
|
||||
var f = filters.parseString('(cn:caseIgnoreMatch:=Dino)');
|
||||
t.ok(f);
|
||||
t.ok(f.matchType);
|
||||
t.equal(f.matchingRule, 'caseIgnoreMatch');
|
||||
t.equal(f.matchValue, 'Dino');
|
||||
t.ok(f.matches({cn: 'dino'}));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('parse case substrings', function (t) {
|
||||
var f = filters.parseString('(cn:caseIgnoreSubstringsMatch:=*i*o)');
|
||||
t.ok(f);
|
||||
t.ok(f.matchType);
|
||||
t.equal(f.matchingRule, 'caseIgnoreSubstringsMatch');
|
||||
t.ok(f.any);
|
||||
t.ok(f['final']);
|
||||
t.ok(f.matches({cn: 'dino'}));
|
||||
t.end();
|
||||
});
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
|
||||
|
||||
var test = require('tape').test;
|
||||
|
||||
|
||||
///--- Globals
|
||||
|
||||
var Filter;
|
||||
|
||||
|
||||
///--- Tests
|
||||
|
||||
test('load library', function (t) {
|
||||
var filters = require('../../lib/index').filters;
|
||||
t.ok(filters);
|
||||
Filter = filters.Filter;
|
||||
t.ok(Filter);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('multi_test array', function (t) {
|
||||
var rule = function (item) {
|
||||
return (item == 3);
|
||||
};
|
||||
t.ok(Filter.multi_test(rule, [1, 2, 3]));
|
||||
t.ok(!Filter.multi_test(rule, [1, 2]));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('multi_test value', function (t) {
|
||||
var rule = function (item) {
|
||||
return (item == 3);
|
||||
};
|
||||
t.ok(Filter.multi_test(rule, 3));
|
||||
t.ok(!Filter.multi_test(rule, 1));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('get_attr_caseless exact match', function (t) {
|
||||
var f = Filter.get_attr_caseless;
|
||||
t.equal(f({attr: 'testval'}, 'attr'), 'testval');
|
||||
t.equal(f({attr: 'testval'}, 'missing'), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('get_attr_caseless insensitive match', function (t) {
|
||||
var f = Filter.get_attr_caseless;
|
||||
var data = {
|
||||
lower: 'lower',
|
||||
UPPER: 'upper',
|
||||
MiXeD: 'mixed'
|
||||
};
|
||||
t.equal(f(data, 'lower'), 'lower');
|
||||
t.equal(f(data, 'upper'), 'upper');
|
||||
t.equal(f(data, 'mixed'), 'mixed');
|
||||
t.equal(f(data, 'missing'), null);
|
||||
t.end();
|
||||
});
|
Loading…
Reference in New Issue