@ldapjs/filter
Version:
API for handling LDAP-style filters
246 lines (210 loc) • 6.39 kB
JavaScript
'use strict'
const tap = require('tap')
const { search } = require('@ldapjs/protocol')
const Attribute = require('@ldapjs/attribute')
const EqualityFilter = require('./equality')
tap.test('Construct invalid args', async t => {
t.throws(
() => new EqualityFilter(),
Error('attribute must be a string of at least one character')
)
t.throws(
() => new EqualityFilter({ attribute: 'foo' }),
Error('must either provide a buffer via `raw` or some `value`')
)
})
tap.test('Construct args', async t => {
const f = new EqualityFilter({
attribute: 'foo',
value: 'bar'
})
t.ok(f)
t.equal(Object.prototype.toString.call(f), '[object FilterString]')
t.equal(f.TAG, search.FILTER_EQUALITY)
t.equal(f.attribute, 'foo')
t.equal(f.type, 'EqualityFilter')
t.equal(f.value, 'bar')
t.equal(f.toString(), '(foo=bar)')
})
tap.test('construct with raw', async t => {
const f = new EqualityFilter({
attribute: 'foo',
raw: Buffer.from([126])
})
t.ok(f)
t.equal(f.value, '~')
})
tap.test('value setter', async t => {
let data = Buffer.from([126])
const f = new EqualityFilter({ attribute: 'foo', raw: data })
f.value = 'a'
t.not(f.value, data.toString(), 'preserve buffer')
data = Buffer.from('a')
f.value = data.toString()
t.equal(f.value, data.toString(), 'convert string')
f.value = true
t.equal(typeof (f.value), 'boolean', 'preserve other type')
t.ok(f.value)
})
tap.test('toString', t => {
t.test('buffer', async t => {
const f = new EqualityFilter({ attribute: 'foo', raw: Buffer.from('bar') })
t.equal(f.toString(), '(foo=bar)')
})
t.test('string', async t => {
const f = new EqualityFilter({ attribute: 'foo', raw: 'bar' })
t.equal(f.toString(), '(foo=bar)')
})
t.test('boolean', async t => {
const f = new EqualityFilter({ attribute: 'foo', raw: true })
t.throws(
() => f.toString(),
Error('invalid value type')
)
})
t.test('escape value only in toString()', async t => {
const f = new EqualityFilter({
attribute: 'foo',
value: 'ba(r)'
})
t.ok(f)
t.equal(f.attribute, 'foo')
t.equal(f.value, 'ba(r)')
t.equal(f.toString(), '(foo=ba\\28r\\29)')
})
t.end()
})
tap.test('matches', t => {
t.test('match true', async t => {
const f = new EqualityFilter({
attribute: 'foo',
value: 'bar'
})
t.ok(f)
t.equal(f.matches({ foo: 'bar' }), true)
})
t.test('match multiple', async t => {
const f = new EqualityFilter({
attribute: 'foo',
value: 'bar'
})
t.ok(f)
t.equal(f.matches({ foo: ['plop', 'bar'] }), true)
})
t.test('match false', async t => {
const f = new EqualityFilter({
attribute: 'foo',
value: 'bar'
})
t.ok(f)
t.equal(f.matches({ foo: 'baz' }), false)
})
t.test('obj can be array of attributes', async t => {
const attributes = Attribute.fromObject({ cn: 'foo' })
const f = new EqualityFilter({
attribute: 'cn',
value: 'foo'
})
t.equal(f.matches(attributes), true)
})
t.test('throws if element not an attribute', async t => {
const f = new EqualityFilter({
attribute: 'cn',
value: 'foo'
})
t.throws(
() => f.matches([{ cn: 'foo' }]),
'array element must be an instance of LdapAttribute'
)
})
t.end()
})
tap.test('escape EqualityFilter inputs', async t => {
const f = new EqualityFilter({
attribute: '(|(foo',
value: 'bar))('
})
t.equal(f.attribute, '(|(foo')
t.equal(f.value, 'bar))(')
t.equal(f.toString(), '(\\28|\\28foo=bar\\29\\29\\28)')
f.value = Buffer.from([97, 115, 100, 102, 41, 40, 0, 255])
t.equal(f.toString(), '(\\28|\\28foo=asdf\\29\\28\\00ÿ)')
f.value = Buffer.from([195, 40])
t.equal(f.toString(), '(\\28|\\28foo=\\c3\\28)')
f.value = Buffer.from([195, 177])
t.equal(f.toString(), '(\\28|\\28foo=\\c3\\b1)')
})
tap.test('encodes to BER correctly', t => {
t.test('basic ascii values', async t => {
const expected = Buffer.from([
0xa3, 0x0a,
0x04, 0x03, 0x66, 0x6f, 0x6f, // OctetString "foo"
0x04, 0x03, 0x62, 0x61, 0x72 // OctetString "bar"
])
const f = new EqualityFilter({ attribute: 'foo', value: 'bar' })
const ber = f.toBer()
t.equal(expected.compare(ber.buffer), 0)
})
t.test('values with non-ascii', async t => {
const expected = Buffer.from([
0xa3, 0x0b,
0x04, 0x03, 0x66, 0x6f, 0x6f, // OctetString "foo"
0x04, 0x04, 0xc3, 0xad, 0xc3, 0xb8 // OctetString "íø"
])
const f = new EqualityFilter({ attribute: 'foo', value: 'íø' })
const ber = f.toBer()
t.equal(expected.compare(ber.buffer), 0)
})
t.end()
})
tap.test('#parse', t => {
t.test('parses buffer', async t => {
const input = Buffer.from([
0xa3, 0x0a,
0x04, 0x03, 0x66, 0x6f, 0x6f, // OctetString "foo"
0x04, 0x03, 0x62, 0x61, 0x72 // OctetString "bar"
])
const f = EqualityFilter.parse(input)
t.equal(f.attribute, 'foo')
t.equal(f.value, 'bar')
t.equal(f.toString(), '(foo=bar)')
})
t.test('throws for unexpected sequence', async t => {
const input = Buffer.from([
0xa4, 0x0a,
0x04, 0x03, 0x66, 0x6f, 0x6f, // OctetString "foo"
0x04, 0x03, 0x62, 0x61, 0x72 // OctetString "bar"
])
t.throws(
() => EqualityFilter.parse(input),
Error('expected equality filter sequence 0xa3, got 0xa4')
)
})
t.end()
})
tap.test('original node-ldap tests', t => {
// This set of subtests are from the original "filters/eq" test suite
// in the core `ldapjs` module code.
t.test('GH-277 objectClass should be case-insensitive', async t => {
const f = new EqualityFilter({
attribute: 'objectClass',
value: 'CaseInsensitiveObj'
})
t.ok(f)
t.equal(f.matches({ objectClass: 'CaseInsensitiveObj' }), true)
t.equal(f.matches({ OBJECTCLASS: 'CASEINSENSITIVEOBJ' }), true)
t.equal(f.matches({ objectclass: 'caseinsensitiveobj' }), true)
t.equal(f.matches({ objectclass: 'matchless' }), false)
})
t.test('escape EqualityFilter inputs', function (t) {
const f = new EqualityFilter({
attribute: '(|(foo',
value: 'bar))('
})
t.equal(f.attribute, '(|(foo')
t.equal(f.value, 'bar))(')
t.equal(f.toString(), '(\\28|\\28foo=bar\\29\\29\\28)')
t.end()
})
t.end()
})