PageRenderTime 27ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/node_modules/ldapjs/lib/filters/index.js

https://github.com/tsschnoc/gapps_script_shared
JavaScript | 484 lines | 348 code | 68 blank | 68 comment | 83 complexity | 308e78a12fad3e18320784dd16dd983f MD5 | raw file
  1. // Copyright 2011 Mark Cavage, Inc. All rights reserved.
  2. var assert = require('assert');
  3. var asn1 = require('asn1');
  4. var Protocol = require('../protocol');
  5. var Filter = require('./filter');
  6. var AndFilter = require('./and_filter');
  7. var ApproximateFilter = require('./approx_filter');
  8. var EqualityFilter = require('./equality_filter');
  9. var ExtensibleFilter = require('./ext_filter');
  10. var GreaterThanEqualsFilter = require('./ge_filter');
  11. var LessThanEqualsFilter = require('./le_filter');
  12. var NotFilter = require('./not_filter');
  13. var OrFilter = require('./or_filter');
  14. var PresenceFilter = require('./presence_filter');
  15. var SubstringFilter = require('./substr_filter');
  16. ///--- Globals
  17. var BerReader = asn1.BerReader;
  18. ///--- Internal Parsers
  19. // expression parsing
  20. // returns the index of the closing parenthesis matching the open paren
  21. // specified by openParenIndex
  22. function matchParens(str, openParenIndex) {
  23. var stack = [];
  24. var esc = false;
  25. for (var i = openParenIndex || 0; i < str.length; i++) {
  26. var c = str[i];
  27. if (c === '\\') {
  28. if (!esc)
  29. esc = true;
  30. continue;
  31. } else if (c === '(' && !esc) {
  32. stack.push(1);
  33. } else if (c === ')' && !esc) {
  34. stack.pop();
  35. if (stack.length === 0)
  36. return i;
  37. }
  38. esc = false;
  39. }
  40. return str.length - 1;
  41. }
  42. // recursive function that builds a filter tree from a string expression
  43. // the filter tree is an intermediary step between the incoming expression and
  44. // the outgoing Filter Class structure.
  45. function _buildFilterTree(expr) {
  46. var c;
  47. var child;
  48. var clean = false;
  49. var endParen;
  50. var esc = false;
  51. var i = 0;
  52. var tree = {};
  53. var split;
  54. var substrNdx = 0;
  55. var val = '';
  56. if (expr.length === 0)
  57. return tree;
  58. // Chop the parens (the call to matchParens below gets rid of the trailer)
  59. if (expr.charAt(0) == '(')
  60. expr = expr.substring(1, expr.length - 1);
  61. //store prefix operator
  62. if (expr.charAt(0) === '&') {
  63. tree.op = 'and';
  64. expr = expr.substring(1);
  65. } else if (expr.charAt(0) === '|') {
  66. tree.op = 'or';
  67. expr = expr.substring(1);
  68. } else if (expr.charAt(0) === '!') {
  69. tree.op = 'not';
  70. expr = expr.substring(1);
  71. } else {
  72. tree.op = 'expr';
  73. }
  74. if (tree.op != 'expr') {
  75. tree.children = [];
  76. // logical operators are k-ary, so we go until our expression string runs
  77. // out (at least for this recursion level)
  78. while (expr.length !== 0) {
  79. endParen = matchParens(expr);
  80. if (endParen == expr.length - 1) {
  81. tree.children[i] = _buildFilterTree(expr);
  82. expr = '';
  83. } else {
  84. child = expr.slice(0, endParen + 1);
  85. expr = expr.substring(endParen + 1);
  86. tree.children[i] = _buildFilterTree(child);
  87. }
  88. i++;
  89. }
  90. } else {
  91. //else its some sort of non-logical expression, parse and return as such
  92. var operatorStr = '';
  93. tree.name = '';
  94. tree.value = '';
  95. if (expr.indexOf('~=') !== -1) {
  96. operatorStr = '~=';
  97. tree.tag = 'approxMatch';
  98. } else if (expr.indexOf('>=') !== -1) {
  99. operatorStr = '>=';
  100. tree.tag = 'greaterOrEqual';
  101. } else if (expr.indexOf('<=') !== -1) {
  102. operatorStr = '<=';
  103. tree.tag = 'lessOrEqual';
  104. } else if (expr.indexOf(':=') !== -1) {
  105. operatorStr = ':=';
  106. tree.tag = 'extensibleMatch';
  107. } else if (expr.indexOf('=') !== -1) {
  108. operatorStr = '=';
  109. tree.tag = 'equalityMatch';
  110. } else {
  111. tree.tag = 'present';
  112. }
  113. if (operatorStr === '') {
  114. tree.name = expr;
  115. } else {
  116. // pull out lhs and rhs of equality operator
  117. var splitAry = expr.split(operatorStr);
  118. tree.name = splitAry.shift();
  119. tree.value = splitAry.join(operatorStr);
  120. // substrings fall into the equality bin in the
  121. // switch above so we need more processing here
  122. if (tree.tag === 'equalityMatch') {
  123. if (tree.value === '*') {
  124. tree.tag = 'present';
  125. } else {
  126. // Effectively a hand-rolled .shift() to support \* sequences
  127. clean = true;
  128. split = [];
  129. substrNdx = 0;
  130. split[substrNdx] = '';
  131. for (i = 0; i < tree.value.length; i++) {
  132. c = tree.value[i];
  133. if (esc) {
  134. split[substrNdx] += c;
  135. esc = false;
  136. } else if (c === '*') {
  137. split[++substrNdx] = '';
  138. } else if (c === '\\') {
  139. esc = true;
  140. } else {
  141. split[substrNdx] += c;
  142. }
  143. }
  144. if (split.length > 1) {
  145. tree.tag = 'substrings';
  146. clean = true;
  147. // if the value string doesn't start with a * then theres no initial
  148. // value else split will have an empty string in its first array
  149. // index...
  150. // we need to remove that empty string
  151. if (tree.value.indexOf('*') !== 0) {
  152. tree.initial = split.shift();
  153. } else {
  154. split.shift();
  155. }
  156. // if the value string doesn't end with a * then theres no final
  157. // value also same split stuff as the initial stuff above
  158. if (tree.value.lastIndexOf('*') !== tree.value.length - 1) {
  159. tree['final'] = split.pop();
  160. } else {
  161. split.pop();
  162. }
  163. tree.any = split;
  164. } else {
  165. tree.value = split[0]; // pick up the cleaned version
  166. }
  167. }
  168. } else if (tree.tag == 'extensibleMatch') {
  169. split = tree.name.split(':');
  170. tree.extensible = {
  171. matchType: split[0],
  172. value: tree.value
  173. };
  174. switch (split.length) {
  175. case 1:
  176. break;
  177. case 2:
  178. if (split[1].toLowerCase() === 'dn') {
  179. tree.extensible.dnAttributes = true;
  180. } else {
  181. tree.extensible.rule = split[1];
  182. }
  183. break;
  184. case 3:
  185. tree.extensible.dnAttributes = true;
  186. tree.extensible.rule = split[2];
  187. break;
  188. default:
  189. throw new Error('Invalid extensible filter');
  190. }
  191. }
  192. }
  193. // Cleanup any escape sequences
  194. if (!clean) {
  195. for (i = 0; i < tree.value.length; i++) {
  196. c = tree.value[i];
  197. if (esc) {
  198. val += c;
  199. esc = false;
  200. } else if (c === '\\') {
  201. esc = true;
  202. } else {
  203. val += c;
  204. }
  205. }
  206. tree.value = val;
  207. }
  208. }
  209. return tree;
  210. }
  211. function serializeTree(tree, filter) {
  212. if (tree === undefined || tree.length === 0)
  213. return;
  214. // if the current tree object is not an expression then its a logical
  215. // operator (ie an internal node in the tree)
  216. var current = null;
  217. if (tree.op !== 'expr') {
  218. switch (tree.op) {
  219. case 'and':
  220. current = new AndFilter();
  221. break;
  222. case 'or':
  223. current = new OrFilter();
  224. break;
  225. case 'not':
  226. current = new NotFilter();
  227. break;
  228. default:
  229. break;
  230. }
  231. filter.addFilter(current || filter);
  232. if (current || tree.children.length) {
  233. tree.children.forEach(function (child) {
  234. serializeTree(child, current);
  235. });
  236. }
  237. } else {
  238. // else its a leaf node in the tree, and represents some type of
  239. // non-logical expression
  240. var tmp;
  241. // convert the tag name to a filter class type
  242. switch (tree.tag) {
  243. case 'approxMatch':
  244. tmp = new ApproximateFilter({
  245. attribute: tree.name,
  246. value: tree.value
  247. });
  248. break;
  249. case 'extensibleMatch':
  250. tmp = new ExtensibleFilter(tree.extensible);
  251. break;
  252. case 'greaterOrEqual':
  253. tmp = new GreaterThanEqualsFilter({
  254. attribute: tree.name,
  255. value: tree.value
  256. });
  257. break;
  258. case 'lessOrEqual':
  259. tmp = new LessThanEqualsFilter({
  260. attribute: tree.name,
  261. value: tree.value
  262. });
  263. break;
  264. case 'equalityMatch':
  265. tmp = new EqualityFilter({
  266. attribute: tree.name,
  267. value: tree.value
  268. });
  269. break;
  270. case 'substrings':
  271. tmp = new SubstringFilter({
  272. attribute: tree.name,
  273. initial: tree.initial,
  274. any: tree.any,
  275. 'final': tree['final']
  276. });
  277. break;
  278. case 'present':
  279. tmp = new PresenceFilter({
  280. attribute: tree.name
  281. });
  282. break;
  283. default:
  284. break;
  285. }
  286. if (tmp)
  287. filter.addFilter(tmp);
  288. }
  289. }
  290. function _parseString(str) {
  291. assert.ok(str);
  292. // create a blank object to pass into treeToObjs
  293. // since its recursive we have to prime it ourselves.
  294. // this gets stripped off before the filter structure is returned
  295. // at the bottom of this function.
  296. var filterObj = new AndFilter({
  297. filters: []
  298. });
  299. serializeTree(_buildFilterTree(str), filterObj);
  300. return filterObj.filters[0];
  301. }
  302. /*
  303. * A filter looks like this coming in:
  304. * Filter ::= CHOICE {
  305. * and [0] SET OF Filter,
  306. * or [1] SET OF Filter,
  307. * not [2] Filter,
  308. * equalityMatch [3] AttributeValueAssertion,
  309. * substrings [4] SubstringFilter,
  310. * greaterOrEqual [5] AttributeValueAssertion,
  311. * lessOrEqual [6] AttributeValueAssertion,
  312. * present [7] AttributeType,
  313. * approxMatch [8] AttributeValueAssertion,
  314. * extensibleMatch [9] MatchingRuleAssertion --v3 only
  315. * }
  316. *
  317. * SubstringFilter ::= SEQUENCE {
  318. * type AttributeType,
  319. * SEQUENCE OF CHOICE {
  320. * initial [0] IA5String,
  321. * any [1] IA5String,
  322. * final [2] IA5String
  323. * }
  324. * }
  325. *
  326. * The extensibleMatch was added in LDAPv3:
  327. *
  328. * MatchingRuleAssertion ::= SEQUENCE {
  329. * matchingRule [1] MatchingRuleID OPTIONAL,
  330. * type [2] AttributeDescription OPTIONAL,
  331. * matchValue [3] AssertionValue,
  332. * dnAttributes [4] BOOLEAN DEFAULT FALSE
  333. * }
  334. */
  335. function _parse(ber) {
  336. assert.ok(ber);
  337. function parseSet(f) {
  338. var end = ber.offset + ber.length;
  339. while (ber.offset < end)
  340. f.addFilter(_parse(ber));
  341. }
  342. var f;
  343. var type = ber.readSequence();
  344. switch (type) {
  345. case Protocol.FILTER_AND:
  346. f = new AndFilter();
  347. parseSet(f);
  348. break;
  349. case Protocol.FILTER_APPROX:
  350. f = new ApproximateFilter();
  351. f.parse(ber);
  352. break;
  353. case Protocol.FILTER_EQUALITY:
  354. f = new EqualityFilter();
  355. f.parse(ber);
  356. return f;
  357. case Protocol.FILTER_EXT:
  358. f = new ExtensibleFilter();
  359. f.parse(ber);
  360. return f;
  361. case Protocol.FILTER_GE:
  362. f = new GreaterThanEqualsFilter();
  363. f.parse(ber);
  364. return f;
  365. case Protocol.FILTER_LE:
  366. f = new LessThanEqualsFilter();
  367. f.parse(ber);
  368. return f;
  369. case Protocol.FILTER_NOT:
  370. var _f = _parse(ber);
  371. f = new NotFilter({
  372. filter: _f
  373. });
  374. break;
  375. case Protocol.FILTER_OR:
  376. f = new OrFilter();
  377. parseSet(f);
  378. break;
  379. case Protocol.FILTER_PRESENT:
  380. f = new PresenceFilter();
  381. f.parse(ber);
  382. break;
  383. case Protocol.FILTER_SUBSTRINGS:
  384. f = new SubstringFilter();
  385. f.parse(ber);
  386. break;
  387. default:
  388. throw new Error('Invalid search filter type: 0x' + type.toString(16));
  389. }
  390. assert.ok(f);
  391. return f;
  392. }
  393. ///--- API
  394. module.exports = {
  395. parse: function (ber) {
  396. if (!ber || !(ber instanceof BerReader))
  397. throw new TypeError('ber (BerReader) required');
  398. return _parse(ber);
  399. },
  400. parseString: function (filter) {
  401. if (!filter || typeof (filter) !== 'string')
  402. throw new TypeError('filter (string) required');
  403. return _parseString(filter);
  404. },
  405. AndFilter: AndFilter,
  406. ApproximateFilter: ApproximateFilter,
  407. EqualityFilter: EqualityFilter,
  408. ExtensibleFilter: ExtensibleFilter,
  409. GreaterThanEqualsFilter: GreaterThanEqualsFilter,
  410. LessThanEqualsFilter: LessThanEqualsFilter,
  411. NotFilter: NotFilter,
  412. OrFilter: OrFilter,
  413. PresenceFilter: PresenceFilter,
  414. SubstringFilter: SubstringFilter,
  415. Filter: Filter
  416. };