Skip to content
This repository has been archived by the owner on May 14, 2024. It is now read-only.

Commit

Permalink
Baseline control object implemented
Browse files Browse the repository at this point in the history
  • Loading branch information
jsumners committed Sep 1, 2019
0 parents commit 7eb52e4
Show file tree
Hide file tree
Showing 7 changed files with 459 additions and 0 deletions.
77 changes: 77 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
*.ldif
*.tar.*
*.tgz

# Lock files
pnpm-lock.yaml
shrinkwrap.yaml
package-lock.json
yarn.lock

# Logs
logs
*.log
npm-debug.log*

# Runtime data
pids
*.pid
*.seed

# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov

# Coverage directory used by tools like istanbul
coverage

# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt

# node-waf configuration
.lock-wscript

# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release

# Dependency directory
node_modules

# Optional npm cache directory
.npm

# Optional REPL history
.node_repl_history

# 0x
.__browserify_string_empty.js
profile-*
*.flamegraph

# tap --cov
.nyc_output/

# JetBrains IntelliJ IDEA
.idea/
*.iml

# VS Code
.vscode/

# xcode
build/*
*.mode1
*.mode1v3
*.mode2v3
*.perspective
*.perspectivev3
*.pbxuser
*.xcworkspace
xcuserdata

# macOS
.DS_Store

# keys
*.pem
*.env.json
*.env
4 changes: 4 additions & 0 deletions .taprc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
esm: false

files:
- 'test/**/*.test.js'
62 changes: 62 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict'

const { Ber } = require('asn1')
const Control = require('./lib/control')

module.exports = {

getControl: function getControl (ber) {
if (!ber) throw TypeError('ber must be provided')

if (ber.readSequence() === null) { return null }

let type
const opts = {
criticality: false,
value: null
}

if (ber.length) {
const end = ber.offset + ber.length

type = ber.readString()
if (ber.offset < end) {
if (ber.peek() === Ber.Boolean) { opts.criticality = ber.readBoolean() }
}

if (ber.offset < end) { opts.value = ber.readString(Ber.OctetString, true) }
}

let control
switch (type) {
// case PersistentSearchControl.OID:
// control = new PersistentSearchControl(opts)
// break
// case EntryChangeNotificationControl.OID:
// control = new EntryChangeNotificationControl(opts)
// break
// case PagedResultsControl.OID:
// control = new PagedResultsControl(opts)
// break
// case ServerSideSortingRequestControl.OID:
// control = new ServerSideSortingRequestControl(opts)
// break
// case ServerSideSortingResponseControl.OID:
// control = new ServerSideSortingResponseControl(opts)
// break
default:
opts.type = type
control = Control(opts)
break
}

return control
},

Control
// EntryChangeNotificationControl: EntryChangeNotificationControl,
// PagedResultsControl: PagedResultsControl,
// PersistentSearchControl: PersistentSearchControl,
// ServerSideSortingRequestControl: ServerSideSortingRequestControl,
// ServerSideSortingResponseControl: ServerSideSortingResponseControl
}
84 changes: 84 additions & 0 deletions lib/control.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
'use strict'

const { BerWriter } = require('asn1')

module.exports = Control

/**
* Baseline LDAP control object. Implements
* https://tools.ietf.org/html/rfc4511#section-4.1.11
*
* @param {object} [options]
* @param {string} [options.type=''] The dotted decimal control type value.
* @param {boolean} [options.criticality=false] Criticality value for the control.
* @param {string|Buffer} [options.value] The value for the control. If this is
* a `string` then it will be written as-is. If it is an instance of `Buffer`
* then it will be written by `value.toString()`.
*
* @typedef {object} Control
*/
function Control (options) {
if ((this instanceof Control) === false) return new Control(options)

const opts = Object.assign({ type: '', criticality: false, value: null }, options)
this.type = opts.type
this.criticality = opts.criticality
this.value = opts.value
}

/**
* Serializes the control instance into an LDAP style control object, e.g.
* `type` => `controlType`. If an instance has a `_json(obj)` method then the
* built object will be sent to that method and the resulting mutated object
* returned.
*
* @alias json
* @memberof! Control#
*/
Object.defineProperty(Control.prototype, 'json', {
get: function getJson () {
const obj = {
controlType: this.type,
criticality: this.criticality,
controlValue: this.value
}
return (typeof (this._json) === 'function' ? this._json(obj) : obj)
}
})

/**
* Converts the instance into a [BER](http://luca.ntop.org/Teaching/Appunti/asn1.html)
* representation.
*
* @param {BerWriter} [ber] An empty `BerWriter` instance to populate.
*
* @returns {object} A BER object.
*/
Control.prototype.toBer = function toBer (ber = new BerWriter()) {
ber.startSequence()
ber.writeString(this.type || '')
ber.writeBoolean(this.criticality)
if (typeof (this._toBer) === 'function') {
this._toBer(ber)
} else {
if (this.value) {
if (typeof this.value === 'string') {
ber.writeString(this.value)
} else if (Buffer.isBuffer(this.value)) {
ber.writeString(this.value.toString())
}
}
}

ber.endSequence()
return ber
}

/**
* Alias for {@link Control#json}.
*
* @returns {object}
*/
Control.prototype.toString = function toString () {
return this.json
}
40 changes: 40 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
{
"name": "@ldapjs/controls",
"version": "1.0.0",
"description": "LDAP control objects",
"main": "index.js",
"scripts": {
"lint": "standard | snazzy",
"lint:ci": "standard",
"test": "tap --no-cov",
"test:cov": "tap",
"test:cov:html": "tap --coverage-report=html"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/ldapjs/controls.git"
},
"keywords": [
"ldapjs"
],
"author": "James Sumners",
"license": "MIT",
"bugs": {
"url": "https://github.com/ldapjs/controls/issues"
},
"homepage": "https://github.com/ldapjs/controls#readme",
"devDependencies": {
"husky": "^3.0.4",
"snazzy": "^8.0.0",
"standard": "^14.1.0",
"tap": "^14.6.1"
},
"dependencies": {
"asn1": "^0.2.4"
},
"husky": {
"hooks": {
"pre-commit": "npm run lint && npm run test"
}
}
}
55 changes: 55 additions & 0 deletions test/index.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
'use strict'

const tap = require('tap')
const { BerReader, BerWriter } = require('asn1')
const controls = require('../')

tap.test('#getControl', t => {
t.test('requires a BER to parse', async t => {
try {
controls.getControl()
t.fail('should throw exception')
} catch (error) {
t.match(error, /ber must be provided/)
}
})

t.test('returns null for empty BER', async t => {
const result = controls.getControl(new BerReader(Buffer.alloc(0)))
t.is(result, null)
})

t.test('parses a BER (control)', async t => {
const ber = new BerWriter()
ber.startSequence()
ber.writeString('2.16.840.1.113730.3.4.2')
ber.writeBoolean(true)
ber.writeString('foo')
ber.endSequence()

const control = controls.getControl(new BerReader(ber.buffer))

t.ok(control)
t.equal(control.type, '2.16.840.1.113730.3.4.2')
t.ok(control.criticality)
t.equal(control.value.toString('utf8'), 'foo')
t.end()
})

t.test('parses BER with no value', function (t) {
const ber = new BerWriter()
ber.startSequence()
ber.writeString('2.16.840.1.113730.3.4.2')
ber.endSequence()

const control = controls.getControl(new BerReader(ber.buffer))

t.ok(control)
t.equal(control.type, '2.16.840.1.113730.3.4.2')
t.equal(control.criticality, false)
t.notOk(control.value, null)
t.end()
})

t.end()
})

0 comments on commit 7eb52e4

Please sign in to comment.