@ldapjs/messages
Version:
API for creating and parsing LDAP messages
317 lines (275 loc) • 7.76 kB
JavaScript
'use strict'
const tap = require('tap')
const Attribute = require('@ldapjs/attribute')
const { BerReader, BerWriter } = require('@ldapjs/asn1')
const { DN } = require('@ldapjs/dn')
const AddRequest = require('./add-request')
const { addRequestBytes } = require('./_fixtures/message-byte-arrays')
tap.test('basic', t => {
t.test('constructor no args', async t => {
const req = new AddRequest()
t.strictSame(req.pojo, {
messageId: 1,
protocolOp: 0x68,
type: 'AddRequest',
entry: null,
attributes: [],
controls: []
})
})
t.test('constructor with args', async t => {
const req = new AddRequest({
entry: 'dn=foo,dc=example,dc=com',
attributes: [{
type: 'cn',
values: ['foo']
}]
})
t.strictSame(req.pojo, {
messageId: 1,
protocolOp: 0x68,
type: 'AddRequest',
entry: 'dn=foo,dc=example,dc=com',
attributes: [{
type: 'cn',
values: ['foo']
}],
controls: []
})
})
t.end()
})
tap.test('.attributes', t => {
t.test('returns a copy of the attributes', async t => {
const inputAttrs = [{ type: 'cn', values: ['foo'] }]
const req = new AddRequest({
entry: 'cn=foo',
attributes: inputAttrs
})
const attrs = req.attributes
t.not(attrs, inputAttrs)
t.equal(attrs.length, inputAttrs.length)
})
t.test('replaces attributes list', async t => {
const req = new AddRequest({
entry: 'cn=foo',
attributes: [new Attribute({ type: 'cn', values: ['foo'] })]
})
t.strictSame(req.attributes[0].pojo, { type: 'cn', values: ['foo'] })
req.attributes = [new Attribute({ type: 'sn', values: ['bar'] })]
t.strictSame(req.attributes[0].pojo, { type: 'sn', values: ['bar'] })
})
t.test('throws if not an array', async t => {
const input = { type: 'cn', values: ['foo'] }
t.throws(
() => new AddRequest({ entry: 'cn=foo', attributes: input }),
Error('attrs must be an array')
)
})
t.test('throws if attribute is invalid', async t => {
const input = [{ type: 42, values: ['foo'] }]
t.throws(
() => new AddRequest({ entry: 'cn=foo', attributes: input }),
Error('attr must be an Attribute instance or Attribute-like object')
)
})
t.end()
})
tap.test('.entry', t => {
t.test('gets and sets', async t => {
let req = new AddRequest({ entry: 'cn=foo' })
t.equal(req.entry.toString(), 'cn=foo')
t.equal(req._dn.toString(), 'cn=foo')
req.entry = 'sn=bar'
t.equal(req.entry.toString(), 'sn=bar')
t.equal(req._dn.toString(), 'sn=bar')
req.entry = DN.fromString('cn=baz')
t.equal(req.entry.toString(), 'cn=baz')
req = new AddRequest()
t.equal(req.entry, null)
})
t.test('throws for bad value', async t => {
const req = new AddRequest()
t.throws(
() => {
req.entry = { cn: 'foo' }
},
'entry must be a valid DN string or instance of LdapDn'
)
})
t.end()
})
tap.test('addAttribute', t => {
t.test('throws for invalid input', async t => {
const req = new AddRequest({ entry: 'cn=foo' })
t.throws(
() => req.addAttribute({ type: 'foo', values: ['foo'] }),
Error('attr must be an instance of Attribute')
)
})
t.test('adds an attribute', async t => {
const req = new AddRequest({ entry: 'cn=foo' })
req.addAttribute(new Attribute({ type: 'bar', values: ['baz'] }))
t.strictSame(req.pojo, {
messageId: 1,
protocolOp: 0x68,
type: 'AddRequest',
entry: 'cn=foo',
attributes: [{
type: 'bar',
values: ['baz']
}],
controls: []
})
})
t.end()
})
tap.test('attributeNames', t => {
t.test('returns the names list', async t => {
const req = new AddRequest({
entry: 'cn=foo',
attributes: [
new Attribute({ type: 'bar' }),
new Attribute({ type: 'baz' }),
new Attribute({ type: 'foobar' }),
new Attribute({ type: 'barfoo' })
]
})
t.strictSame(req.attributeNames(), [
'bar',
'baz',
'foobar',
'barfoo'
])
})
t.end()
})
tap.test('getAttribute', t => {
t.test('throws for invalid parameter', async t => {
const req = new AddRequest({ entry: 'cn=foo' })
t.throws(
() => req.getAttribute(42),
Error('attributeName must be a string')
)
})
t.test('returns null for not found', async t => {
const req = new AddRequest({ entry: 'cn=foo' })
t.equal(req.getAttribute('bar'), null)
})
t.test('returns the correct attribute', async t => {
const req = new AddRequest({
entry: 'cn=foo',
attributes: [
new Attribute({ type: 'bar' }),
new Attribute({ type: 'baz', values: ['baz', 'baz', 'baz'] }),
new Attribute({ type: 'foobar' }),
new Attribute({ type: 'barfoo' })
]
})
const attr = req.getAttribute('baz')
t.strictSame(attr.pojo, {
type: 'baz',
values: ['baz', 'baz', 'baz']
})
})
t.end()
})
tap.test('indexOf', t => {
t.test('throws for invalid parameter', async t => {
const req = new AddRequest({ entry: 'cn=foo' })
t.throws(
() => req.indexOf(42),
Error('attributeName must be a string')
)
})
t.test('finds an attribute', async t => {
const req = new AddRequest({
entry: 'cn=foo',
attributes: [
new Attribute({ type: 'bar' }),
new Attribute({ type: 'baz' }),
new Attribute({ type: 'foobar' }),
new Attribute({ type: 'barfoo' })
]
})
t.equal(req.indexOf('foobar'), 2)
})
t.test('returns for not found', async t => {
const req = new AddRequest({
entry: 'cn=foo',
attributes: [
new Attribute({ type: 'bar' }),
new Attribute({ type: 'baz' }),
new Attribute({ type: 'foobar' }),
new Attribute({ type: 'barfoo' })
]
})
t.equal(req.indexOf('zifbang'), -1)
})
t.end()
})
tap.test('_toBer', t => {
tap.test('converts instance to BER', async t => {
const req = new AddRequest({
entry: 'dc=example,dc=com',
attributes: [
new Attribute({ type: 'objectclass', values: ['top', 'domain'] }),
new Attribute({ type: 'dc', values: ['example'] })
]
})
const writer = new BerWriter()
req._toBer(writer)
t.equal(
Buffer.from(addRequestBytes.slice(5)).compare(writer.buffer),
0
)
})
t.end()
})
tap.test('_pojo', t => {
t.test('returns a pojo representation', async t => {
const req = new AddRequest({
entry: 'cn=foo',
attributes: [{ type: 'bar', values: ['baz'] }]
})
t.strictSame(req._pojo(), {
entry: 'cn=foo',
attributes: [{
type: 'bar',
values: ['baz']
}]
})
})
t.end()
})
tap.test('#parseToPojo', t => {
t.test('throws if operation incorrect', async t => {
const reqBuffer = Buffer.from(addRequestBytes)
reqBuffer[5] = 0x61
const reader = new BerReader(reqBuffer)
reader.readSequence()
reader.readInt()
t.throws(
() => AddRequest.parseToPojo(reader),
Error('found wrong protocol operation: 0x61')
)
})
t.test('returns a pojo representation', async t => {
const reqBuffer = Buffer.from(addRequestBytes)
const reader = new BerReader(reqBuffer)
reader.readSequence()
reader.readInt()
const pojo = AddRequest.parseToPojo(reader)
t.equal(pojo.protocolOp, 0x68)
t.equal(pojo.entry, 'dc=example,dc=com')
t.strictSame(pojo.attributes[0].pojo, {
type: 'objectclass',
values: ['top', 'domain']
})
t.strictSame(pojo.attributes[1].pojo, {
type: 'dc',
values: ['example']
})
})
t.end()
})