lint cleanup, and extensible matching
This commit is contained in:
parent
60870497ef
commit
fb63ba2220
|
@ -28,249 +28,271 @@ 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 = [];
|
||||
|
||||
for(var i=openParenIndex || 0;i<str.length;i++){
|
||||
if(str.charAt(i)=='('){
|
||||
stack.push(1);
|
||||
}else if(str.charAt(i) == ')'){
|
||||
stack.pop();
|
||||
if(stack.length === 0){
|
||||
//console.log('[_findMatchingParenthesis]: returning ', i);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
//console.log('[_findMatchingParenthesis]: returning ', str.length-1);
|
||||
return str.length-1;
|
||||
};
|
||||
// expression parsing
|
||||
// returns the index of the closing parenthesis matching the open paren
|
||||
// specified by openParenIndex
|
||||
function matchParens(str, openParenIndex) {
|
||||
var stack = [];
|
||||
|
||||
for (var i = openParenIndex || 0; i < str.length; i++) {
|
||||
if (str.charAt(i) === '(') {
|
||||
stack.push(1);
|
||||
} else if (str.charAt(i) === ')') {
|
||||
stack.pop();
|
||||
if (stack.length === 0)
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return str.length - 1;
|
||||
}
|
||||
|
||||
|
||||
//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){
|
||||
//console.log('[buildFilterTree]: expression: ',expr);
|
||||
var tree = {};
|
||||
if(expr.length === 0){
|
||||
return tree;
|
||||
}
|
||||
//console.log(expr);
|
||||
|
||||
//remove leading and trailing parenthesis if they are there
|
||||
if (expr.charAt(0) == '('){
|
||||
expr = expr.substring(1,expr.length-1);
|
||||
console.log('substring: '+expr);
|
||||
}
|
||||
|
||||
//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 its a logical operator
|
||||
if(tree.op != 'expr'){
|
||||
var child,i=0;
|
||||
tree.children = [];
|
||||
|
||||
//logical operators are k-ary, so we go until our expression string runs out
|
||||
//(at least for this recursion level)
|
||||
var endParen;
|
||||
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
|
||||
|
||||
//tag represents the name of the operator, initially this library was used to
|
||||
//encode a filter string into DSML where the tag name would be <DSML:equalityMatch ...>
|
||||
//so thats why its named tag, if you were wondering...
|
||||
var operatorStr = "";
|
||||
var valueOffset=0;
|
||||
tree.name = "";
|
||||
tree.value = '';
|
||||
if(expr.indexOf('~=') !=-1 ){
|
||||
operatorStr = '~=';
|
||||
tree.tag = 'approxMatch';
|
||||
valueOffset = 2;
|
||||
}else if(expr.indexOf('>=') != -1){
|
||||
operatorStr = '>=';
|
||||
tree.tag = 'greaterOrEqual';
|
||||
valueOffset = 2;
|
||||
}else if(expr.indexOf('<=') != -1){
|
||||
operatorStr = '<=';
|
||||
tree.tag = 'lessOrEqual';
|
||||
valueOffset = 2;
|
||||
}else if(expr.indexOf("=") != -1){
|
||||
operatorStr = '=';
|
||||
tree.tag = 'equalityMatch';
|
||||
valueOffset = 1;
|
||||
}else {
|
||||
tree.tag = 'present';
|
||||
}
|
||||
console.log('operatorStr: ' + operatorStr + '; valueOffset: ' + valueOffset);
|
||||
if (operatorStr == ""){
|
||||
tree.name = expr;
|
||||
}else{
|
||||
//pull out lhs and rhs of equality operator
|
||||
//var index = expr.indexOf('=');
|
||||
|
||||
var splitAry = expr.split(operatorStr,2);
|
||||
tree.name =splitAry[0];
|
||||
tree.value = splitAry[1];
|
||||
|
||||
//substrings fall into the equality bin in the switch above
|
||||
//so we need another check here to look for *
|
||||
if (tree.value.indexOf('*') != -1 && tree.tag == 'equalityMatch'){
|
||||
tree.tag = 'substrings';
|
||||
|
||||
var split = tree.value.split("*");
|
||||
|
||||
//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){
|
||||
tree.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){
|
||||
tree['final'] = split.pop();
|
||||
}else{
|
||||
split.pop();
|
||||
}
|
||||
tree.any = split;
|
||||
}
|
||||
if(tree.value.length == 0){
|
||||
tree.tag = 'present';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
};
|
||||
// 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 tree = {};
|
||||
var split;
|
||||
|
||||
var treeToObjs = function(tree,filterObj){
|
||||
//console.log("in treeToObjs");
|
||||
if(tree === undefined){
|
||||
return filterObj;
|
||||
}
|
||||
|
||||
if(tree.length === 0){
|
||||
return filterObj;
|
||||
}
|
||||
|
||||
//if the current tree object is not an expression then its a logical operator (ie an internal node in the tree)
|
||||
var currentFilter = filterObj;
|
||||
if(tree.op != "expr"){
|
||||
console.log("adding "+tree.op+" to filters");
|
||||
switch (tree.op){
|
||||
case "and":
|
||||
filterObj.addFilter(currentFilter = new AndFilter({filters:[]}));
|
||||
break;
|
||||
case "or":
|
||||
filterObj.addFilter(currentFilter = new OrFilter({filters:[]}));
|
||||
break;
|
||||
case "not":
|
||||
filterObj.addFilter(currentFilter = new NotFilter({filters:[]}));
|
||||
break;
|
||||
}
|
||||
for(var i = 0,tempFilter,child;i<tree.children.length;i++){
|
||||
child = tree.children[i];
|
||||
treeToObjs(child,currentFilter);
|
||||
}
|
||||
}else{
|
||||
//else its a leaf node in the tree, and represents some type of non-logical expression
|
||||
var tempFilter;
|
||||
|
||||
//convert the tag name to a filter class type
|
||||
switch(tree.tag){
|
||||
case "approxMatch":
|
||||
tempFilter = new ApproximateFilter({
|
||||
attribute: tree.name,
|
||||
value: tree.value
|
||||
});
|
||||
//console.log("adding "+tree.tag+"; attr: "+tree.name+"; value: "+tree.value);
|
||||
break;
|
||||
case "greaterOrEqual":
|
||||
tempFilter = new GreaterThanEqualsFilter({
|
||||
attribute: tree.name,
|
||||
value: tree.value
|
||||
});
|
||||
//console.log("adding "+tree.tag+"; attr: "+tree.name+"; value: "+tree.value);
|
||||
break;
|
||||
case "lessOrEqual":
|
||||
tempFilter = new LessThanEqualsFilter({
|
||||
attribute: tree.name,
|
||||
value: tree.value
|
||||
});
|
||||
//console.log("adding "+tree.tag+"; attr: "+tree.name+"; value: "+tree.value);
|
||||
break;
|
||||
case "equalityMatch":
|
||||
tempFilter = new EqualityFilter({
|
||||
attribute: tree.name,
|
||||
value: tree.value
|
||||
});
|
||||
//console.log("adding "+tree.tag+"; attr: "+tree.name+"; value: "+tree.value);
|
||||
break;
|
||||
case "substrings":
|
||||
tempFilter = new SubstringFilter({
|
||||
attribute: tree.name,
|
||||
initial: tree.initial,
|
||||
any: tree.any,
|
||||
"final": tree["final"]
|
||||
});
|
||||
//console.log("adding "+tree.tag+"; attr: "+tree.name+"; initial: "+tree.initial+"; any: "+JSON.stringify(tree.any) + "; final: "+tree['final']);
|
||||
break;
|
||||
case "present":
|
||||
tempFilter = new PresenceFilter({
|
||||
attribute: tree.name
|
||||
});
|
||||
//console.log("adding "+tree.tag+"; attr: "+tree.name);
|
||||
break;
|
||||
}
|
||||
filterObj.addFilter(tempFilter);
|
||||
}
|
||||
};
|
||||
if (expr.length === 0)
|
||||
return tree;
|
||||
|
||||
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') {
|
||||
var child;
|
||||
var i = 0;
|
||||
tree.children = [];
|
||||
|
||||
// logical operators are k-ary, so we go until our expression string runs
|
||||
// out (at least for this recursion level)
|
||||
var endParen;
|
||||
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
|
||||
|
||||
// tag represents the name of the operator, initially this library was used
|
||||
// to encode a filter string into DSML where the tag name would be
|
||||
// <DSML:equalityMatch ...>
|
||||
// so thats why its named tag, if you were wondering...
|
||||
var operatorStr = '';
|
||||
var valueOffset = 0;
|
||||
tree.name = '';
|
||||
tree.value = '';
|
||||
if (expr.indexOf('~=') !== -1) {
|
||||
operatorStr = '~=';
|
||||
tree.tag = 'approxMatch';
|
||||
valueOffset = 2;
|
||||
} else if (expr.indexOf('>=') !== -1) {
|
||||
operatorStr = '>=';
|
||||
tree.tag = 'greaterOrEqual';
|
||||
valueOffset = 2;
|
||||
} else if (expr.indexOf('<=') !== -1) {
|
||||
operatorStr = '<=';
|
||||
tree.tag = 'lessOrEqual';
|
||||
valueOffset = 2;
|
||||
} else if (expr.indexOf(':=') !== -1) {
|
||||
operatorStr = ':=';
|
||||
tree.tag = 'extensibleMatch';
|
||||
valueOffset = 2;
|
||||
} else if (expr.indexOf('=') !== -1) {
|
||||
operatorStr = '=';
|
||||
tree.tag = 'equalityMatch';
|
||||
valueOffset = 1;
|
||||
} else {
|
||||
tree.tag = 'present';
|
||||
}
|
||||
|
||||
if (operatorStr === '') {
|
||||
tree.name = expr;
|
||||
} else {
|
||||
// pull out lhs and rhs of equality operator
|
||||
var splitAry = expr.split(operatorStr, 2);
|
||||
tree.name = splitAry[0];
|
||||
tree.value = splitAry[1];
|
||||
|
||||
// substrings and extensible matching fall into the equality bin in the
|
||||
// switch above so we need more processing here
|
||||
if (tree.tag === 'equalityMatch') {
|
||||
if (tree.value.indexOf('*') !== -1) {
|
||||
tree.tag = 'substrings';
|
||||
|
||||
split = tree.value.split('*');
|
||||
|
||||
// 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) {
|
||||
tree.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) {
|
||||
tree['final'] = split.pop();
|
||||
} else {
|
||||
split.pop();
|
||||
}
|
||||
tree.any = split;
|
||||
} else if (tree.value.length === 0) {
|
||||
tree.tag = 'present';
|
||||
}
|
||||
} else if (tree.tag == 'extensibleMatch') {
|
||||
split = tree.name.split(':');
|
||||
console.log(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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
||||
|
||||
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:[]
|
||||
});
|
||||
|
||||
var tree = _buildFilterTree(str);
|
||||
//console.log("tree built: ",JSON.stringify(tree));
|
||||
treeToObjs(tree,filterObj);
|
||||
return filterObj.filters[0];
|
||||
};
|
||||
function serializeTree(tree, filter) {
|
||||
if (tree === undefined || tree.length === 0)
|
||||
return filter;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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:
|
||||
|
|
|
@ -425,14 +425,14 @@ function Server(options) {
|
|||
log.trace('data on %s: %s', c.ldap.id, util.inspect(data));
|
||||
|
||||
try {
|
||||
c.parser.write(data);
|
||||
c.parser.write(data);
|
||||
} catch (e) {
|
||||
log.warn('Unable to parse message [c.on(\'data\')]: %s', e.stack);
|
||||
c.end(new LDAPResult({
|
||||
status: 0x02,
|
||||
errorMessage: e.toString(),
|
||||
connection: c
|
||||
}).toBer());
|
||||
log.warn('Unable to parse message [c.on(\'data\')]: %s', e.stack);
|
||||
c.end(new LDAPResult({
|
||||
status: 0x02,
|
||||
errorMessage: e.toString(),
|
||||
connection: c
|
||||
}).toBer());
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -108,3 +108,4 @@ test('parse RFC example 5', function(t) {
|
|||
t.ok(f.dnAttributes);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue