Overhaul DN parsing and formatting
Certain applications depend upon DN string formatting in a manner more strict than specified in the RFC. To enable format transcription and reproduction, some changes were made to how DNs are converted to/from strings. - Store RDN strings raw instead of escaped - Record formatting details during DN/RDN parsing - Add DN.format method to control format recreation - Remove DN.spaced method in favor of DN.setFormat Fix mcavage/node-ldapjs#176
This commit is contained in:
parent
22b04f3a94
commit
495ea2afce
|
@ -24,6 +24,10 @@
|
|||
- #178 Perform strict presence testing on attribute vals
|
||||
- #183 Accept buffers or strings for cert/key in createServer
|
||||
- #180 Add '-i, --insecure' option and to all ldapjs-\* CLIs
|
||||
- Completely overhaul DN parsing/formatting
|
||||
- Add options for format preservation
|
||||
- Removed `spaced()` and `rndSpaced` from DN API
|
||||
- Fix parent/child rules regarding empty DNs
|
||||
|
||||
## 0.7.1
|
||||
|
||||
|
|
339
lib/dn.js
339
lib/dn.js
|
@ -23,29 +23,143 @@ function isWhitespace(c) {
|
|||
|
||||
function RDN(obj) {
|
||||
var self = this;
|
||||
this.attrs = {};
|
||||
|
||||
if (obj) {
|
||||
Object.keys(obj).forEach(function (k) {
|
||||
self[k.toLowerCase()] = obj[k];
|
||||
self.set(k, obj[k]);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
RDN.prototype.toString = function () {
|
||||
RDN.prototype.set = function set(name, value, opts) {
|
||||
if (typeof (name) !== 'string')
|
||||
throw new TypeError('name (string) required');
|
||||
if (typeof (value) !== 'string')
|
||||
throw new TypeError('value (string) required');
|
||||
var self = this;
|
||||
var lname = name.toLowerCase();
|
||||
this.attrs[lname] = {
|
||||
value: value,
|
||||
name: name
|
||||
};
|
||||
if (opts && typeof (opts) === 'object') {
|
||||
Object.keys(opts).forEach(function (k) {
|
||||
if (k !== 'value')
|
||||
self.attrs[lname][k] = opts[k];
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
RDN.prototype.equals = function equals(rdn) {
|
||||
if (typeof (rdn) !== 'object')
|
||||
return false;
|
||||
|
||||
var ourKeys = Object.keys(this.attrs);
|
||||
var theirKeys = Object.keys(rdn.attrs);
|
||||
if (ourKeys.length !== theirKeys.length)
|
||||
return false;
|
||||
|
||||
ourKeys.sort();
|
||||
theirKeys.sort();
|
||||
|
||||
for (var i = 0; i < ourKeys.length; i++) {
|
||||
if (ourKeys[i] !== theirKeys[i])
|
||||
return false;
|
||||
if (this.attrs[ourKeys[i]].value !== rdn.attrs[ourKeys[i]].value)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Convert RDN to string according to specified formatting options.
|
||||
* (see: DN.format for option details)
|
||||
*/
|
||||
RDN.prototype.format = function format(options) {
|
||||
if (options) {
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
var self = this;
|
||||
var str = '';
|
||||
Object.keys(this).forEach(function (k) {
|
||||
|
||||
function escapeValue(val, forceQuote) {
|
||||
var out = '';
|
||||
var cur = 0;
|
||||
var len = val.length;
|
||||
var quoted = false;
|
||||
/* BEGIN JSSTYLED */
|
||||
var escaped = /[\\\"]/;
|
||||
var special = /[,=+<>#;]/;
|
||||
/* END JSSTYLED */
|
||||
|
||||
if (len > 0) {
|
||||
// Wrap strings with trailing or leading spaces in quotes
|
||||
quoted = forceQuote || (val[0] == ' ' || val[len-1] == ' ');
|
||||
}
|
||||
|
||||
while (cur < len) {
|
||||
if (escaped.test(val[cur]) || (!quoted && special.test(val[cur]))) {
|
||||
out += '\\';
|
||||
}
|
||||
out += val[cur++];
|
||||
}
|
||||
if (quoted)
|
||||
out = '"' + out + '"';
|
||||
return out;
|
||||
}
|
||||
function sortParsed(a, b) {
|
||||
return self.attrs[a].order - self.attrs[b].order;
|
||||
}
|
||||
function sortStandard(a, b) {
|
||||
var nameCompare = a.localeCompare(b);
|
||||
if (nameCompare === 0) {
|
||||
// TODO: Handle binary values
|
||||
return self.attrs[a].value.localeCompare(self.attrs[b].value);
|
||||
} else {
|
||||
return nameCompare;
|
||||
}
|
||||
}
|
||||
|
||||
var keys = Object.keys(this.attrs);
|
||||
if (options.keepOrder) {
|
||||
keys.sort(sortParsed);
|
||||
} else {
|
||||
keys.sort(sortStandard);
|
||||
}
|
||||
|
||||
keys.forEach(function (key) {
|
||||
var attr = self.attrs[key];
|
||||
if (str.length)
|
||||
str += '+';
|
||||
|
||||
str += k + '=' + self[k];
|
||||
if (options.keepCase) {
|
||||
str += attr.name;
|
||||
} else {
|
||||
if (options.upperName)
|
||||
str += key.toUpperCase();
|
||||
else
|
||||
str += key;
|
||||
}
|
||||
|
||||
str += '=' + escapeValue(attr.value, (options.keepQuote && attr.quoted));
|
||||
});
|
||||
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
RDN.prototype.toString = function toString() {
|
||||
return this.format();
|
||||
};
|
||||
|
||||
|
||||
// Thank you OpenJDK!
|
||||
function parse(name) {
|
||||
if (typeof (name) !== 'string')
|
||||
|
@ -53,27 +167,30 @@ function parse(name) {
|
|||
|
||||
var cur = 0;
|
||||
var len = name.length;
|
||||
var rdnSpaced = false;
|
||||
|
||||
function parseRdn() {
|
||||
var rdn = new RDN();
|
||||
var order = 0;
|
||||
rdn.spLead = trim();
|
||||
while (cur < len) {
|
||||
var leading = trim();
|
||||
rdnSpaced = rdnSpaced || (leading > 0);
|
||||
var opts = {
|
||||
order: order
|
||||
};
|
||||
var attr = parseAttrType();
|
||||
trim();
|
||||
if (cur >= len || name[cur++] !== '=')
|
||||
throw invalidDN(name);
|
||||
|
||||
trim();
|
||||
var value = parseAttrValue();
|
||||
trim();
|
||||
rdn[attr.toLowerCase()] = value;
|
||||
// Parameters about RDN value are set in 'opts' by parseAttrValue
|
||||
var value = parseAttrValue(opts);
|
||||
rdn.set(attr, value, opts);
|
||||
rdn.spTrail = trim();
|
||||
if (cur >= len || name[cur] !== '+')
|
||||
break;
|
||||
++cur;
|
||||
++order;
|
||||
}
|
||||
|
||||
return rdn;
|
||||
}
|
||||
|
||||
|
@ -110,10 +227,12 @@ function parse(name) {
|
|||
return name.slice(beg, cur);
|
||||
}
|
||||
|
||||
function parseAttrValue() {
|
||||
function parseAttrValue(opts) {
|
||||
if (cur < len && name[cur] == '#') {
|
||||
opts.binary = true;
|
||||
return parseBinaryAttrValue();
|
||||
} else if (cur < len && name[cur] == '"') {
|
||||
opts.quoted = true;
|
||||
return parseQuotedAttrValue();
|
||||
} else {
|
||||
return parseStringAttrValue();
|
||||
|
@ -129,41 +248,43 @@ function parse(name) {
|
|||
}
|
||||
|
||||
function parseQuotedAttrValue() {
|
||||
var beg = cur++;
|
||||
var str = '';
|
||||
++cur; // Consume the first quote
|
||||
|
||||
while ((cur < len) && name[cur] != '"') {
|
||||
if (name[cur] === '\\')
|
||||
++cur; // consume backslash, then what follows
|
||||
|
||||
++cur;
|
||||
cur++;
|
||||
str += name[cur++];
|
||||
}
|
||||
if (cur++ >= len) // no closing quote
|
||||
throw invalidDN(name);
|
||||
|
||||
return name.slice(beg, cur);
|
||||
return str;
|
||||
}
|
||||
|
||||
function parseStringAttrValue() {
|
||||
var beg = cur;
|
||||
var str = '';
|
||||
var esc = -1;
|
||||
|
||||
while ((cur < len) && !atTerminator()) {
|
||||
if (name[cur] === '\\') {
|
||||
++cur; // consume backslash, then what follows
|
||||
esc = cur;
|
||||
// Consume the backslash and mark its place just in case it's escaping
|
||||
// whitespace which needs to be preserved.
|
||||
esc = cur++;
|
||||
}
|
||||
++cur;
|
||||
if (cur === len) // backslash followed by nothing
|
||||
throw invalidDN(name);
|
||||
str += name[cur++];
|
||||
}
|
||||
if (cur > len) // backslash followed by nothing
|
||||
throw invalidDN(name);
|
||||
|
||||
// Trim off (unescaped) trailing whitespace.
|
||||
var end;
|
||||
for (end = cur; end > beg; end--) {
|
||||
if (!isWhitespace(name[end - 1]) || (esc === (end - 1)))
|
||||
// Trim off (unescaped) trailing whitespace and rewind cursor to the end of
|
||||
// the AttrValue to record whitespace length.
|
||||
for (; cur > beg; cur--) {
|
||||
if (!isWhitespace(name[cur - 1]) || (esc === (cur - 1)))
|
||||
break;
|
||||
}
|
||||
return name.slice(beg, end);
|
||||
return str.slice(0, cur - beg);
|
||||
}
|
||||
|
||||
function atTerminator() {
|
||||
|
@ -175,6 +296,10 @@ function parse(name) {
|
|||
|
||||
var rdns = [];
|
||||
|
||||
// Short-circuit for empty DNs
|
||||
if (len === 0)
|
||||
return new DN(rdns);
|
||||
|
||||
rdns.push(parseRdn());
|
||||
while (cur < len) {
|
||||
if (name[cur] === ',' || name[cur] === ';') {
|
||||
|
@ -185,7 +310,7 @@ function parse(name) {
|
|||
}
|
||||
}
|
||||
|
||||
return new DN(rdns).spaced(rdnSpaced);
|
||||
return new DN(rdns);
|
||||
}
|
||||
|
||||
|
||||
|
@ -202,7 +327,7 @@ function DN(rdns) {
|
|||
});
|
||||
|
||||
this.rdns = rdns.slice();
|
||||
this.rdnSpaced = true;
|
||||
this._format = {};
|
||||
|
||||
this.__defineGetter__('length', function () {
|
||||
return this.rdns.length;
|
||||
|
@ -210,75 +335,109 @@ function DN(rdns) {
|
|||
}
|
||||
|
||||
|
||||
DN.prototype.toString = function () {
|
||||
var _dn = [];
|
||||
this.rdns.forEach(function (rdn) {
|
||||
_dn.push(rdn.toString());
|
||||
});
|
||||
return _dn.join(this.rdnSpaced ? ', ' : ',');
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.spaced = function (spaces) {
|
||||
this.rdnSpaced = (spaces === false) ? false : true;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.childOf = function (dn) {
|
||||
if (typeof (dn) !== 'object')
|
||||
dn = parse(dn);
|
||||
|
||||
if (this.rdns.length <= dn.rdns.length)
|
||||
return false;
|
||||
|
||||
var diff = this.rdns.length - dn.rdns.length;
|
||||
for (var i = dn.rdns.length - 1; i >= 0; i--) {
|
||||
var rdn = dn.rdns[i];
|
||||
|
||||
var keys = Object.keys(rdn);
|
||||
if (!keys.length)
|
||||
return false;
|
||||
|
||||
for (var j = 0; j < keys.length; j++) {
|
||||
var k = keys[j];
|
||||
var ourRdn = this.rdns[i + diff];
|
||||
if (ourRdn[k] !== rdn[k])
|
||||
return false;
|
||||
}
|
||||
/**
|
||||
* Convert DN to string according to specified formatting options.
|
||||
*
|
||||
* Parameters:
|
||||
* - options: formatting parameters (optional, details below)
|
||||
*
|
||||
* Options are divided into two types:
|
||||
* - Preservation options: Using data recorded during parsing, details of the
|
||||
* original DN are preserved when converting back into a string.
|
||||
* - Modification options: Alter string formatting defaults.
|
||||
*
|
||||
* Preservation options _always_ take precedence over modification options.
|
||||
*
|
||||
* Preservation Options:
|
||||
* - keepOrder: Order of multi-value RDNs.
|
||||
* - keepQuote: RDN values which were quoted will remain so.
|
||||
* - keepSpace: Leading/trailing spaces will be output.
|
||||
* - keepCase: Parsed attr name will be output instead of lowercased version.
|
||||
*
|
||||
* Modification Options:
|
||||
* - upperName: RDN names will be uppercased instead of lowercased.
|
||||
* - skipSpace: Disable trailing space after RDN separators
|
||||
*/
|
||||
DN.prototype.format = function (options) {
|
||||
if (options) {
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
} else {
|
||||
options = this._format;
|
||||
}
|
||||
|
||||
return true;
|
||||
var str = '';
|
||||
function repeatChar(c, n) {
|
||||
var out = '';
|
||||
var max = n ? n : 0;
|
||||
for (var i = 0; i < max; i++)
|
||||
out += c;
|
||||
return out;
|
||||
}
|
||||
this.rdns.forEach(function (rdn) {
|
||||
var rdnString = rdn.format(options);
|
||||
if (str.length !== 0) {
|
||||
str += ',';
|
||||
}
|
||||
if (options.keepSpace) {
|
||||
str += (repeatChar(' ', rdn.spLead) +
|
||||
rdnString + repeatChar(' ', rdn.spTrail));
|
||||
} else if (options.skipSpace === true || str.length === 0) {
|
||||
str += rdnString;
|
||||
} else {
|
||||
str += ' ' + rdnString;
|
||||
}
|
||||
});
|
||||
return str;
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.parentOf = function (dn) {
|
||||
/**
|
||||
* Set default string formatting options.
|
||||
*/
|
||||
DN.prototype.setFormat = function setFormat(options) {
|
||||
if (typeof (options) !== 'object')
|
||||
throw new TypeError('options must be an object');
|
||||
this._format = options;
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.toString = function () {
|
||||
return this.format();
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.parentOf = function parentOf(dn) {
|
||||
if (typeof (dn) !== 'object')
|
||||
dn = parse(dn);
|
||||
|
||||
if (!this.rdns.length || this.rdns.length >= dn.rdns.length)
|
||||
if (this.rdns.length >= dn.rdns.length)
|
||||
return false;
|
||||
|
||||
var diff = dn.rdns.length - this.rdns.length;
|
||||
for (var i = this.rdns.length - 1; i >= 0; i--) {
|
||||
var rdn = this.rdns[i];
|
||||
var keys = Object.keys(rdn);
|
||||
var myRDN = this.rdns[i];
|
||||
var theirRDN = dn.rdns[i + diff];
|
||||
|
||||
if (!keys.length)
|
||||
if (!myRDN.equals(theirRDN))
|
||||
return false;
|
||||
|
||||
for (var j = 0; j < keys.length; j++) {
|
||||
var k = keys[j];
|
||||
var theirRdn = dn.rdns[i + diff];
|
||||
if (theirRdn[k] !== rdn[k])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.childOf = function childOf(dn) {
|
||||
if (typeof (dn) !== 'object')
|
||||
dn = parse(dn);
|
||||
return dn.parentOf(this);
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.isEmpty = function isEmpty() {
|
||||
return (this.rdns.length === 0);
|
||||
};
|
||||
|
||||
|
||||
DN.prototype.equals = function (dn) {
|
||||
if (typeof (dn) !== 'object')
|
||||
dn = parse(dn);
|
||||
|
@ -287,22 +446,8 @@ DN.prototype.equals = function (dn) {
|
|||
return false;
|
||||
|
||||
for (var i = 0; i < this.rdns.length; i++) {
|
||||
var ours = this.rdns[i];
|
||||
var theirs = dn.rdns[i];
|
||||
var ourKeys = Object.keys(ours);
|
||||
var theirKeys = Object.keys(theirs);
|
||||
if (ourKeys.length !== theirKeys.length)
|
||||
if (!this.rdns[i].equals(dn.rdns[i]))
|
||||
return false;
|
||||
|
||||
ourKeys.sort();
|
||||
theirKeys.sort();
|
||||
|
||||
for (var j = 0; j < ourKeys.length; j++) {
|
||||
if (ourKeys[j] !== theirKeys[j])
|
||||
return false;
|
||||
if (ours[ourKeys[j]] !== theirs[ourKeys[j]])
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -310,7 +455,7 @@ DN.prototype.equals = function (dn) {
|
|||
|
||||
|
||||
DN.prototype.parent = function () {
|
||||
if (this.rdns.length > 1) {
|
||||
if (this.rdns.length !== 0) {
|
||||
var save = this.rdns.shift();
|
||||
var dn = new DN(this.rdns);
|
||||
this.rdns.unshift(save);
|
||||
|
@ -322,7 +467,9 @@ DN.prototype.parent = function () {
|
|||
|
||||
|
||||
DN.prototype.clone = function () {
|
||||
return new DN(this.rdns);
|
||||
var dn = new DN(this.rdns);
|
||||
dn._format = this._format;
|
||||
return dn;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -775,7 +775,7 @@ Server.prototype._sortedRouteKeys = function _sortedRouteKeys() {
|
|||
if (_dn instanceof dn.DN) {
|
||||
var reversed = _dn.clone();
|
||||
reversed.rdns.reverse();
|
||||
reversedRDNsToKeys[reversed.spaced(true).toString()] = key;
|
||||
reversedRDNsToKeys[reversed.format()] = key;
|
||||
}
|
||||
});
|
||||
var output = [];
|
||||
|
|
116
test/dn.test.js
116
test/dn.test.js
|
@ -66,6 +66,7 @@ test('parse compound', function (t) {
|
|||
|
||||
test('parse quoted', function (t) {
|
||||
var DN_STR = 'cn="mark+sn=cavage", ou=people, o=joyent';
|
||||
var ESCAPE_STR = 'cn=mark\\+sn\\=cavage, ou=people, o=joyent';
|
||||
var name = dn.parse(DN_STR);
|
||||
t.ok(name);
|
||||
t.ok(name.rdns);
|
||||
|
@ -74,27 +75,27 @@ test('parse quoted', function (t) {
|
|||
name.rdns.forEach(function (rdn) {
|
||||
t.equal('object', typeof (rdn));
|
||||
});
|
||||
t.equal(name.toString(), DN_STR);
|
||||
t.equal(name.toString(), ESCAPE_STR);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('equals', function (t) {
|
||||
var dn1 = dn.parse('cn=foo, dc=bar');
|
||||
t.ok(dn1.equals('cn=foo, dc=bar'));
|
||||
t.ok(!dn1.equals('cn=foo1, dc=bar'));
|
||||
t.ok(dn1.equals(dn.parse('cn=foo, dc=bar')));
|
||||
t.ok(!dn1.equals(dn.parse('cn=foo2, dc=bar')));
|
||||
var dn1 = dn.parse('cn=foo,dc=bar');
|
||||
t.ok(dn1.equals('cn=foo,dc=bar'));
|
||||
t.ok(!dn1.equals('cn=foo1,dc=bar'));
|
||||
t.ok(dn1.equals(dn.parse('cn=foo,dc=bar')));
|
||||
t.ok(!dn1.equals(dn.parse('cn=foo2,dc=bar')));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('child of', function (t) {
|
||||
var dn1 = dn.parse('cn=foo, dc=bar');
|
||||
var dn1 = dn.parse('cn=foo,dc=bar');
|
||||
t.ok(dn1.childOf('dc=bar'));
|
||||
t.ok(!dn1.childOf('dc=moo'));
|
||||
t.ok(!dn1.childOf('dc=foo'));
|
||||
t.ok(!dn1.childOf('cn=foo, dc=bar'));
|
||||
t.ok(!dn1.childOf('cn=foo,dc=bar'));
|
||||
|
||||
t.ok(dn1.childOf(dn.parse('dc=bar')));
|
||||
t.end();
|
||||
|
@ -102,45 +103,104 @@ test('child of', function (t) {
|
|||
|
||||
|
||||
test('parent of', function (t) {
|
||||
var dn1 = dn.parse('cn=foo, dc=bar');
|
||||
t.ok(dn1.parentOf('cn=moo, cn=foo, dc=bar'));
|
||||
t.ok(!dn1.parentOf('cn=moo, cn=bar, dc=foo'));
|
||||
t.ok(!dn1.parentOf('cn=foo, dc=bar'));
|
||||
var dn1 = dn.parse('cn=foo,dc=bar');
|
||||
t.ok(dn1.parentOf('cn=moo,cn=foo,dc=bar'));
|
||||
t.ok(!dn1.parentOf('cn=moo,cn=bar,dc=foo'));
|
||||
t.ok(!dn1.parentOf('cn=foo,dc=bar'));
|
||||
|
||||
t.ok(dn1.parentOf(dn.parse('cn=moo, cn=foo, dc=bar')));
|
||||
t.ok(dn1.parentOf(dn.parse('cn=moo,cn=foo,dc=bar')));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('empty DNs GH-16', function (t) {
|
||||
test('DN parent', function (t) {
|
||||
var _dn = dn.parse('cn=foo,ou=bar');
|
||||
var parent1 = _dn.parent();
|
||||
var parent2 = parent1.parent();
|
||||
t.ok(parent1.equals('ou=bar'));
|
||||
t.ok(parent2.equals(''));
|
||||
t.equal(parent2.parent(), null);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('empty DNs', function (t) {
|
||||
var _dn = dn.parse('');
|
||||
var _dn2 = dn.parse('cn=foo');
|
||||
t.ok(_dn.isEmpty());
|
||||
t.notOk(_dn2.isEmpty());
|
||||
t.notOk(_dn.equals('cn=foo'));
|
||||
t.notOk(_dn2.equals(''));
|
||||
t.notOk(_dn.parentOf('cn=foo'));
|
||||
t.ok(_dn.parentOf('cn=foo'));
|
||||
t.notOk(_dn.childOf('cn=foo'));
|
||||
t.notOk(_dn2.parentOf(''));
|
||||
t.notOk(_dn2.childOf(''));
|
||||
t.ok(_dn2.childOf(''));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('case insensitive attribute names GH-20', function (t) {
|
||||
var dn1 = dn.parse('CN=foo, dc=bar');
|
||||
t.ok(dn1.equals('cn=foo, dc=bar'));
|
||||
t.ok(dn1.equals(dn.parse('cn=foo, DC=bar')));
|
||||
test('case insensitive attribute names', function (t) {
|
||||
var dn1 = dn.parse('CN=foo,dc=bar');
|
||||
t.ok(dn1.equals('cn=foo,dc=bar'));
|
||||
t.ok(dn1.equals(dn.parse('cn=foo,DC=bar')));
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('rdn spacing', function (t) {
|
||||
var dn1 = dn.parse('cn=foo,dc=bar');
|
||||
var dn2 = dn.parse('cn=foo, dc=bar');
|
||||
t.ok(dn1.equals(dn2));
|
||||
t.equals(dn1.toString(), 'cn=foo,dc=bar');
|
||||
t.equals(dn2.toString(), 'cn=foo, dc=bar');
|
||||
t.equals(dn1.spaced().toString(), 'cn=foo, dc=bar');
|
||||
t.equals(dn2.spaced(false).toString(), 'cn=foo,dc=bar');
|
||||
test('format', function (t) {
|
||||
var DN_ORDER = dn.parse('sn=bar+cn=foo,ou=test');
|
||||
var DN_QUOTE = dn.parse('cn="foo",ou=test');
|
||||
var DN_QUOTE2 = dn.parse('cn=" foo",ou=test');
|
||||
var DN_SPACE = dn.parse('cn=foo,ou=test');
|
||||
var DN_SPACE2 = dn.parse('cn=foo ,ou=test');
|
||||
var DN_CASE = dn.parse('CN=foo,Ou=test');
|
||||
|
||||
t.equal(DN_ORDER.format({keepOrder: false}), 'cn=foo+sn=bar, ou=test');
|
||||
t.equal(DN_ORDER.format({keepOrder: true}), 'sn=bar+cn=foo, ou=test');
|
||||
|
||||
t.equal(DN_QUOTE.format({keepQuote: false}), 'cn=foo, ou=test');
|
||||
t.equal(DN_QUOTE.format({keepQuote: true}), 'cn="foo", ou=test');
|
||||
t.equal(DN_QUOTE2.format({keepQuote: false}), 'cn=" foo", ou=test');
|
||||
t.equal(DN_QUOTE2.format({keepQuote: true}), 'cn=" foo", ou=test');
|
||||
|
||||
t.equal(DN_SPACE.format({keepSpace: false}), 'cn=foo, ou=test');
|
||||
t.equal(DN_SPACE.format({keepSpace: true}), 'cn=foo,ou=test');
|
||||
t.equal(DN_SPACE.format({skipSpace: true}), 'cn=foo,ou=test');
|
||||
t.equal(DN_SPACE2.format({keepSpace: false}), 'cn=foo, ou=test');
|
||||
t.equal(DN_SPACE2.format({keepSpace: true}), 'cn=foo ,ou=test');
|
||||
t.equal(DN_SPACE2.format({skipSpace: true}), 'cn=foo,ou=test');
|
||||
|
||||
t.equal(DN_CASE.format({keepCase: false}), 'cn=foo, ou=test');
|
||||
t.equal(DN_CASE.format({keepCase: true}), 'CN=foo, Ou=test');
|
||||
t.equal(DN_CASE.format({upperName: true}), 'CN=foo, OU=test');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('set format', function (t) {
|
||||
var _dn = dn.parse('uid="user", sn=bar+cn=foo, dc=test , DC=com');
|
||||
t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test, dc=com');
|
||||
_dn.setFormat({keepOrder: true});
|
||||
t.equal(_dn.toString(), 'uid=user, sn=bar+cn=foo, dc=test, dc=com');
|
||||
_dn.setFormat({keepQuote: true});
|
||||
t.equal(_dn.toString(), 'uid="user", cn=foo+sn=bar, dc=test, dc=com');
|
||||
_dn.setFormat({keepSpace: true});
|
||||
t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test , dc=com');
|
||||
_dn.setFormat({keepCase: true});
|
||||
t.equal(_dn.toString(), 'uid=user, cn=foo+sn=bar, dc=test, DC=com');
|
||||
_dn.setFormat({upperName: true});
|
||||
t.equal(_dn.toString(), 'UID=user, CN=foo+SN=bar, DC=test, DC=com');
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
||||
test('format persists across clone', function (t) {
|
||||
var _dn = dn.parse('uid="user", sn=bar+cn=foo, dc=test , DC=com');
|
||||
var OUT = 'UID="user", CN=foo+SN=bar, DC=test, DC=com';
|
||||
_dn.setFormat({keepQuote: true, upperName: true});
|
||||
var clone = _dn.clone();
|
||||
t.equals(_dn.toString(), OUT);
|
||||
t.equals(clone.toString(), OUT);
|
||||
t.end();
|
||||
});
|
||||
|
||||
|
|
Loading…
Reference in New Issue