GH-28 Support extensible filters (client only)

This commit is contained in:
Mark Cavage 2011-11-08 16:12:48 -08:00
parent 920b9e00f4
commit aedd9f222c
3 changed files with 251 additions and 2 deletions

107
lib/filters/ext_filter.js Normal file
View File

@ -0,0 +1,107 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
var assert = require('assert');
var util = require('util');
var Filter = require('./filter');
var Protocol = require('../protocol');
///--- 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.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;
});
}
util.inherits(ExtensibleFilter, Filter);
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');
return false;
};
ExtensibleFilter.prototype.parse = function(ber) {
throw new Error('ExtensibleFilter not supported');
};
ExtensibleFilter.prototype._toBer = function(ber) {
assert.ok(ber);
if (this.rule)
ber.writeString(this.rule, 0x81);
if (this.matchType)
ber.writeString(this.matchType, 0x82);
ber.writeString(this.value, 0x83);
if (this.dnAttributes)
ber.writeBoolean(this.dnAttributes, 0x84);
return ber;
};

View File

@ -10,6 +10,7 @@ var Filter = require('./filter');
var AndFilter = require('./and_filter');
var ApproximateFilter = require('./approx_filter');
var EqualityFilter = require('./equality_filter');
var ExtensibleFilter = require('./ext_filter');
var GreaterThanEqualsFilter = require('./ge_filter');
var LessThanEqualsFilter = require('./le_filter');
var NotFilter = require('./not_filter');
@ -123,7 +124,7 @@ function _parseString(str) {
}));
} else {
switch (stack[i].op) {
case '=': // could be presence, equality or substr
case '=': // could be presence, equality, substr or ext
if (stack[i].value === '*') {
filters.push(new PresenceFilter(stack[i]));
} else {
@ -150,7 +151,32 @@ function _parseString(str) {
}
}
if (vals.length === 1) {
filters.push(new EqualityFilter(stack[i]));
if (stack[i].attribute.indexOf(':') !== -1) {
var extTmp = stack[i].attribute.split(':');
var extOpts = {};
extOpts.matchType = extTmp[0];
switch (extTmp.length) {
case 2:
break;
case 3:
if (extTmp[1].toLowerCase() === 'dn') {
extOpts.dnAttributes = true;
} else {
extOpts.rule = extTmp[1];
}
break;
case 4:
extOpts.dnAttributes = true;
extOpts.rule = extTmp[2];
break;
default:
throw new Error('Invalid extensible filter');
}
extOpts.value = vals[0];
filters.push(new ExtensibleFilter(extOpts));
} else {
filters.push(new EqualityFilter(stack[i]));
}
} else {
filters.push(new SubstringFilter({
attribute: stack[i].attribute,
@ -245,6 +271,11 @@ function _parse(ber) {
f.parse(ber);
return f;
case Protocol.FILTER_EXT:
f = new ExtensibleFilter();
f.parse(ber);
return f;
case Protocol.FILTER_GE:
f = new GreaterThanEqualsFilter();
f.parse(ber);
@ -309,6 +340,7 @@ module.exports = {
AndFilter: AndFilter,
ApproximateFilter: ApproximateFilter,
EqualityFilter: EqualityFilter,
ExtensibleFilter: ExtensibleFilter,
GreaterThanEqualsFilter: GreaterThanEqualsFilter,
LessThanEqualsFilter: LessThanEqualsFilter,
NotFilter: NotFilter,

110
tst/filters/ext.test.js Normal file
View File

@ -0,0 +1,110 @@
// Copyright 2011 Mark Cavage, Inc. All rights reserved.
var test = require('tap').test;
var asn1 = require('asn1');
///--- Globals
var ExtensibleFilter;
var BerReader = asn1.BerReader;
var BerWriter = asn1.BerWriter;
var filters;
///--- Tests
test('load library', function(t) {
filters = require('../../lib/index').filters;
t.ok(filters);
ExtensibleFilter = filters.ExtensibleFilter;
t.ok(ExtensibleFilter);
t.end();
});
test('Construct no args', function(t) {
var f = new ExtensibleFilter();
t.ok(f);
t.end();
});
test('Construct args', function(t) {
var f = new ExtensibleFilter({
matchType: 'foo',
value: 'bar',
});
t.ok(f);
t.equal(f.matchType, 'foo');
t.equal(f.value, 'bar');
t.equal(f.toString(), '(foo:=bar)');
t.end();
});
test('parse RFC example 1', function(t) {
var f = filters.parseString('(cn:caseExactMatch:=Fred Flintstone)');
t.ok(f);
t.equal(f.matchType, 'cn');
t.equal(f.matchingRule, 'caseExactMatch');
t.equal(f.matchValue, 'Fred Flintstone');
t.notOk(f.dnAttributes);
t.end();
});
test('parse RFC example 2', function(t) {
var f = filters.parseString('(cn:=Betty Rubble)');
t.ok(f);
t.equal(f.matchType, 'cn');
t.equal(f.matchValue, 'Betty Rubble');
t.notOk(f.dnAttributes);
t.notOk(f.matchingRule);
t.end();
});
test('parse RFC example 3', function(t) {
var f = filters.parseString('(sn:dn:2.4.6.8.10:=Barney Rubble)');
t.ok(f);
t.equal(f.matchType, 'sn');
t.equal(f.matchingRule, '2.4.6.8.10');
t.equal(f.matchValue, 'Barney Rubble');
t.ok(f.dnAttributes);
t.end();
});
test('parse RFC example 3', function(t) {
var f = filters.parseString('(o:dn:=Ace Industry)');
t.ok(f);
t.equal(f.matchType, 'o');
t.notOk(f.matchingRule);
t.equal(f.matchValue, 'Ace Industry');
t.ok(f.dnAttributes);
t.end();
});
test('parse RFC example 4', function(t) {
var f = filters.parseString('(:1.2.3:=Wilma Flintstone)');
t.ok(f);
t.notOk(f.matchType);
t.equal(f.matchingRule, '1.2.3');
t.equal(f.matchValue, 'Wilma Flintstone');
t.notOk(f.dnAttributes);
t.end();
});
test('parse RFC example 5', function(t) {
var f = filters.parseString('(:DN:2.4.6.8.10:=Dino)');
t.ok(f);
t.notOk(f.matchType);
t.equal(f.matchingRule, '2.4.6.8.10');
t.equal(f.matchValue, 'Dino');
t.ok(f.dnAttributes);
t.end();
});