inmemory.js 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. const ldap = require('../lib/index')
  2. /// --- Shared handlers
  3. function authorize (req, res, next) {
  4. /* Any user may search after bind, only cn=root has full power */
  5. const isSearch = (req instanceof ldap.SearchRequest)
  6. if (!req.connection.ldap.bindDN.equals('cn=root') && !isSearch) { return next(new ldap.InsufficientAccessRightsError()) }
  7. return next()
  8. }
  9. /// --- Globals
  10. const SUFFIX = 'o=smartdc'
  11. const db = {}
  12. const server = ldap.createServer()
  13. server.bind('cn=root', function (req, res, next) {
  14. if (req.dn.toString() !== 'cn=root' || req.credentials !== 'secret') { return next(new ldap.InvalidCredentialsError()) }
  15. res.end()
  16. return next()
  17. })
  18. server.add(SUFFIX, authorize, function (req, res, next) {
  19. const dn = req.dn.toString()
  20. if (db[dn]) { return next(new ldap.EntryAlreadyExistsError(dn)) }
  21. db[dn] = req.toObject().attributes
  22. res.end()
  23. return next()
  24. })
  25. server.bind(SUFFIX, function (req, res, next) {
  26. const dn = req.dn.toString()
  27. if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
  28. if (!db[dn].userpassword) { return next(new ldap.NoSuchAttributeError('userPassword')) }
  29. if (db[dn].userpassword.indexOf(req.credentials) === -1) { return next(new ldap.InvalidCredentialsError()) }
  30. res.end()
  31. return next()
  32. })
  33. server.compare(SUFFIX, authorize, function (req, res, next) {
  34. const dn = req.dn.toString()
  35. if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
  36. if (!db[dn][req.attribute]) { return next(new ldap.NoSuchAttributeError(req.attribute)) }
  37. let matches = false
  38. const vals = db[dn][req.attribute]
  39. for (let i = 0; i < vals.length; i++) {
  40. if (vals[i] === req.value) {
  41. matches = true
  42. break
  43. }
  44. }
  45. res.end(matches)
  46. return next()
  47. })
  48. server.del(SUFFIX, authorize, function (req, res, next) {
  49. const dn = req.dn.toString()
  50. if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
  51. delete db[dn]
  52. res.end()
  53. return next()
  54. })
  55. server.modify(SUFFIX, authorize, function (req, res, next) {
  56. const dn = req.dn.toString()
  57. if (!req.changes.length) { return next(new ldap.ProtocolError('changes required')) }
  58. if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
  59. const entry = db[dn]
  60. let mod
  61. for (let i = 0; i < req.changes.length; i++) {
  62. mod = req.changes[i].modification
  63. switch (req.changes[i].operation) {
  64. case 'replace':
  65. if (!entry[mod.type]) { return next(new ldap.NoSuchAttributeError(mod.type)) }
  66. if (!mod.vals || !mod.vals.length) {
  67. delete entry[mod.type]
  68. } else {
  69. entry[mod.type] = mod.vals
  70. }
  71. break
  72. case 'add':
  73. if (!entry[mod.type]) {
  74. entry[mod.type] = mod.vals
  75. } else {
  76. mod.vals.forEach(function (v) {
  77. if (entry[mod.type].indexOf(v) === -1) { entry[mod.type].push(v) }
  78. })
  79. }
  80. break
  81. case 'delete':
  82. if (!entry[mod.type]) { return next(new ldap.NoSuchAttributeError(mod.type)) }
  83. delete entry[mod.type]
  84. break
  85. }
  86. }
  87. res.end()
  88. return next()
  89. })
  90. server.search(SUFFIX, authorize, function (req, res, next) {
  91. const dn = req.dn.toString()
  92. if (!db[dn]) { return next(new ldap.NoSuchObjectError(dn)) }
  93. let scopeCheck
  94. switch (req.scope) {
  95. case 'base':
  96. if (req.filter.matches(db[dn])) {
  97. res.send({
  98. dn,
  99. attributes: db[dn]
  100. })
  101. }
  102. res.end()
  103. return next()
  104. case 'one':
  105. scopeCheck = function (k) {
  106. if (req.dn.equals(k)) { return true }
  107. const parent = ldap.parseDN(k).parent()
  108. return (parent ? parent.equals(req.dn) : false)
  109. }
  110. break
  111. case 'sub':
  112. scopeCheck = function (k) {
  113. return (req.dn.equals(k) || req.dn.parentOf(k))
  114. }
  115. break
  116. }
  117. Object.keys(db).forEach(function (key) {
  118. if (!scopeCheck(key)) { return }
  119. if (req.filter.matches(db[key])) {
  120. res.send({
  121. dn: key,
  122. attributes: db[key]
  123. })
  124. }
  125. })
  126. res.end()
  127. return next()
  128. })
  129. /// --- Fire it up
  130. server.listen(1389, function () {
  131. console.log('LDAP server up at: %s', server.url)
  132. })