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

Commit 42b146c

Browse files
author
Simon Lorenz
authored
Update substring filter to match spec (#5)
* Update substring filter to match spec * Emit warnings about sub prefix deprecation * Add deprecation date to jsdoc annotation * Fix error code names * Format json based on which constructor args were used
1 parent e70a7cb commit 42b146c

9 files changed

+261
-112
lines changed

lib/ber-parsing/_fixtures/evolution-filter.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const bytes = Buffer.from([
2424
// "cn"
2525
0x63, 0x6e,
2626
0x30, 0x05, // sequence, 5 bytes
27-
0x80, 0x03, // subinitial string, 3 bytes
27+
0x80, 0x03, // initial string, 3 bytes
2828
// "ogo"
2929
0x6f, 0x67, 0x6f,
3030

lib/ber-parsing/index.test.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ tap.test('parses SubstringFilter', async t => {
151151
const f = parse(new BerReader(input))
152152
t.type(f, SubstringFilter)
153153
t.equal(f.attribute, 'foo')
154-
t.equal(f.subInitial, 'bar')
154+
t.equal(f.initial, 'bar')
155155
t.equal(f.toString(), '(foo=bar*)')
156156
})
157157

lib/deprecations.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
const warning = require('process-warning')()
44

5-
warning.create('LdapjsFilterWarning', 'LDAP_FILTER_DEP_001', 'parse is deprecated. Use the parseString function instead.')
5+
const clazz = 'LdapjsFilterWarning'
6+
7+
warning.create(clazz, 'LDAP_FILTER_DEP_001', 'parse is deprecated. Use the parseString function instead.')
8+
9+
warning.create(clazz, 'LDAP_FILTER_DEP_002', 'subInitial is deprecated. Use initial instead.')
10+
11+
warning.create(clazz, 'LDAP_FILTER_DEP_003', 'subAny is deprecated. Use any instead.')
12+
13+
warning.create(clazz, 'LDAP_FILTER_DEP_004', 'subFinal is deprecated. Use final instead.')
614

715
module.exports = warning

lib/filters/or.test.js

+4-4
Original file line numberDiff line numberDiff line change
@@ -115,13 +115,13 @@ tap.test('toBer', t => {
115115
// "cn"
116116
0x63, 0x6e,
117117
0x30, 0x05, // sequence, 5 bytes
118-
0x80, 0x03, // string (subinitial tag), 3 bytes
118+
0x80, 0x03, // string (initial tag), 3 bytes
119119
// "foo"
120120
0x66, 0x6f, 0x6f
121121
])
122122
const subFilter = new SubstringFilter({
123123
attribute: 'cn',
124-
subInitial: 'foo'
124+
initial: 'foo'
125125
})
126126
const f = new OrFilter({ filters: [subFilter] })
127127
const ber = f.toBer()
@@ -164,7 +164,7 @@ tap.test('parse', t => {
164164
// "cn"
165165
0x63, 0x6e,
166166
0x30, 0x05, // sequence, 5 bytes
167-
0x80, 0x03, // string (subinitial tag), 3 bytes
167+
0x80, 0x03, // string (initial tag), 3 bytes
168168
// "foo"
169169
0x66, 0x6f, 0x6f,
170170

@@ -173,7 +173,7 @@ tap.test('parse', t => {
173173
// "sn"
174174
0x73, 0x6e,
175175
0x30, 0x05, // sequence, 5 bytes
176-
0x80, 0x03, // string (subinitial tag), 3 bytes
176+
0x80, 0x03, // string (initial tag), 3 bytes
177177
0x66, 0x6f, 0x6f
178178
])
179179

lib/filters/substring.js

+115-44
Original file line numberDiff line numberDiff line change
@@ -6,93 +6,164 @@ const { search } = require('@ldapjs/protocol')
66
const escapeFilterValue = require('../utils/escape-filter-value')
77
const testValues = require('../utils/test-values')
88
const getAttributeValue = require('../utils/get-attribute-value')
9+
const warning = require('../deprecations')
910

1011
/**
1112
* Represents a filter that matches substrings withing LDAP entry attribute
1213
* values, e.g. `(cn=*f*o*o)`.
1314
*/
1415
class SubstringFilter extends FilterString {
15-
#subInitial
16-
#subAny = []
17-
#subFinal
16+
#initial
17+
#any = []
18+
#final
19+
20+
/**
21+
* Internal helper for backwards compatibility.
22+
* @type {Boolean}
23+
*/
24+
#constructedWithSubPrefix
1825

1926
/**
2027
* @typedef {FilterStringParams} SubstringParams
2128
* @property {string} input.attribute The attribute to test against.
22-
* @property {string} [subInitial] Text that must appear at the start
23-
* of a value and may not overlap any value of `subAny` or `subFinal`.
24-
* @property {string[]} [subAny] Text items that must appear in the
25-
* attribute value that do not overlap with `subInitial`, `subFinal`, or
26-
* any other `subAny` item.
27-
* @property {string} [subFinal] Text that must appear at the end of
28-
* the attribute value. May not overlap with `subInitial` or any `subAny`
29+
* @property {string} [initial] Text that must appear at the start
30+
* of a value and may not overlap any value of `any` or `final`.
31+
* @property {string} [subInitial] Deprecated, use `initial`.
32+
* @property {string[]} [any] Text items that must appear in the
33+
* attribute value that do not overlap with `initial`, `final`, or
34+
* any other `any` item.
35+
* @property {string[]} [subAny] Deprecated, use `any`.
36+
* @property {string} [final] Text that must appear at the end of
37+
* the attribute value. May not overlap with `initial` or any `any`
2938
* item.
39+
* @property {string} [subFinal] Deprecated, use `final`.
3040
*/
3141

3242
/**
3343
* @param {SubstringParams} input
3444
*
3545
* @throws When any input parameter is of an incorrect type.
3646
*/
37-
constructor ({ attribute, subInitial, subAny = [], subFinal } = {}) {
47+
constructor ({ attribute, initial, subInitial, any = [], subAny = [], final, subFinal } = {}) {
48+
if (subInitial) {
49+
warning.emit('LDAP_FILTER_DEP_002')
50+
initial = subInitial
51+
}
52+
53+
if (Array.isArray(subAny) && subAny.length > 0) {
54+
warning.emit('LDAP_FILTER_DEP_003')
55+
any = subAny
56+
}
57+
58+
if (subFinal) {
59+
warning.emit('LDAP_FILTER_DEP_004')
60+
final = subFinal
61+
}
62+
3863
if (typeof attribute !== 'string' || attribute.length < 1) {
3964
throw Error('attribute must be a string of at least one character')
4065
}
66+
if (Array.isArray(any) === false) {
67+
throw Error('any must be an array of items')
68+
}
4169
if (Array.isArray(subAny) === false) {
4270
throw Error('subAny must be an array of items')
4371
}
44-
if (subFinal && typeof subFinal !== 'string') {
45-
throw Error('subFinal must be a string')
72+
if (final && typeof final !== 'string') {
73+
throw Error('final must be a string')
4674
}
4775

4876
super({ attribute })
4977

50-
this.#subInitial = subInitial
51-
Array.prototype.push.apply(this.#subAny, subAny)
52-
this.#subFinal = subFinal
78+
this.#initial = initial
79+
Array.prototype.push.apply(this.#any, any)
80+
this.#final = final
81+
this.#constructedWithSubPrefix = subInitial || subFinal || subAny.length > 0
5382

5483
Object.defineProperties(this, {
5584
TAG: { value: search.FILTER_SUBSTRINGS },
5685
type: { value: 'SubstringFilter' }
5786
})
5887
}
5988

89+
/**
90+
* @type {string}
91+
*/
92+
get initial () {
93+
return this.#initial
94+
}
95+
96+
/**
97+
* @type {string[]}
98+
*/
99+
get any () {
100+
return this.#any
101+
}
102+
103+
/**
104+
* @type {string}
105+
*/
106+
get final () {
107+
return this.#final
108+
}
109+
110+
/**
111+
* @deprecated 2023-06-29 Use `initial` instead.
112+
* @type {string}
113+
*/
60114
get subInitial () {
61-
return this.#subInitial
115+
return this.#initial
62116
}
63117

118+
/**
119+
* @deprecated 2023-06-29 Use `any` instead.
120+
* @type {string[]}
121+
*/
64122
get subAny () {
65-
return this.#subAny
123+
return this.#any
66124
}
67125

126+
/**
127+
* @deprecated 2023-06-29 Use `final` instead.
128+
* @type {string}
129+
*/
68130
get subFinal () {
69-
return this.#subFinal
131+
return this.#final
70132
}
71133

72134
get json () {
73-
return {
74-
type: this.type,
75-
subInitial: this.#subInitial,
76-
subAny: this.#subAny,
77-
subFinal: this.#subFinal
135+
if (this.#constructedWithSubPrefix) {
136+
return {
137+
type: this.type,
138+
subInitial: this.#initial,
139+
subAny: this.#any,
140+
subFinal: this.#final
141+
}
142+
} else {
143+
return {
144+
type: this.type,
145+
initial: this.#initial,
146+
any: this.#any,
147+
final: this.#final
148+
}
78149
}
79150
}
80151

81152
toString () {
82153
let result = '(' + escapeFilterValue(this.attribute) + '='
83154

84-
if (this.#subInitial) {
85-
result += escapeFilterValue(this.#subInitial)
155+
if (this.#initial) {
156+
result += escapeFilterValue(this.#initial)
86157
}
87158

88159
result += '*'
89160

90-
for (const any of this.#subAny) {
161+
for (const any of this.#any) {
91162
result += escapeFilterValue(any) + '*'
92163
}
93164

94-
if (this.#subFinal) {
95-
result += escapeFilterValue(this.#subFinal)
165+
if (this.#final) {
166+
result += escapeFilterValue(this.#final)
96167
}
97168

98169
result += ')'
@@ -105,7 +176,7 @@ class SubstringFilter extends FilterString {
105176
* object.
106177
*
107178
* @example
108-
* const filter = new EqualityFilter({ attribute: 'foo', subInitial: 'bar' })
179+
* const filter = new EqualityFilter({ attribute: 'foo', initial: 'bar' })
109180
* assert.equal(filter.matches({ foo: 'bar' }), true)
110181
*
111182
* @param {object} obj An object to check for match.
@@ -137,11 +208,11 @@ class SubstringFilter extends FilterString {
137208

138209
let re = ''
139210

140-
if (this.#subInitial) { re += '^' + escapeRegExp(this.#subInitial) + '.*' }
141-
this.#subAny.forEach(function (s) {
211+
if (this.#initial) { re += '^' + escapeRegExp(this.#initial) + '.*' }
212+
this.#any.forEach(function (s) {
142213
re += escapeRegExp(s) + '.*'
143214
})
144-
if (this.#subFinal) { re += escapeRegExp(this.#subFinal) + '$' }
215+
if (this.#final) { re += escapeRegExp(this.#final) + '$' }
145216

146217
const matcher = new RegExp(re)
147218
return testValues({
@@ -156,15 +227,15 @@ class SubstringFilter extends FilterString {
156227
ber.writeString(this.attribute)
157228
ber.startSequence()
158229

159-
if (this.#subInitial) { ber.writeString(this.#subInitial, 0x80) }
230+
if (this.#initial) { ber.writeString(this.#initial, 0x80) }
160231

161-
if (this.#subAny.length > 0) {
162-
for (const sub of this.#subAny) {
232+
if (this.#any.length > 0) {
233+
for (const sub of this.#any) {
163234
ber.writeString(sub, 0x81)
164235
}
165236
}
166237

167-
if (this.#subFinal) { ber.writeString(this.#subFinal, 0x82) }
238+
if (this.#final) { ber.writeString(this.#final, 0x82) }
168239

169240
ber.endSequence()
170241

@@ -190,9 +261,9 @@ class SubstringFilter extends FilterString {
190261
throw Error(`expected substring filter sequence ${expected}, got ${found}`)
191262
}
192263

193-
let subInitial
194-
const subAny = []
195-
let subFinal
264+
let initial
265+
const any = []
266+
let final
196267

197268
const attribute = reader.readString()
198269
reader.readSequence()
@@ -204,18 +275,18 @@ class SubstringFilter extends FilterString {
204275
const tag = reader.peek()
205276
switch (tag) {
206277
case 0x80: { // Initial
207-
subInitial = reader.readString(tag)
278+
initial = reader.readString(tag)
208279
break
209280
}
210281

211282
case 0x81: { // Any
212283
const anyVal = reader.readString(tag)
213-
subAny.push(anyVal)
284+
any.push(anyVal)
214285
break
215286
}
216287

217288
case 0x82: { // Final
218-
subFinal = reader.readString(tag)
289+
final = reader.readString(tag)
219290
break
220291
}
221292

@@ -225,7 +296,7 @@ class SubstringFilter extends FilterString {
225296
}
226297
}
227298

228-
return new SubstringFilter({ attribute, subInitial, subAny, subFinal })
299+
return new SubstringFilter({ attribute, initial, any, final })
229300
}
230301
}
231302

0 commit comments

Comments
 (0)