ldapjs / node-ldapjs

LDAP Client and Server API for node.js
http://ldapjs.org
MIT License
1.61k stars 443 forks source link

Operations error with ldap_add in LDAP guide #943

Open Ketho opened 1 year ago

Ketho commented 1 year ago

I'm following the guide but I don't understand why I get an Operations error (1) when adding a new entry with ldapadd.

pi@radiuspi:~/code/ldap $ sudo ldapadd -H ldap://localhost:1389 -x -D cn=root -w secret -f ./user.ldif
adding new entry "cn=ldapjs, ou=users, o=myhost"
ldap_add: Operations error (1)
        matched DN: ou=users,o=myhost

It successfully reads the user.ldif file since it's adding a new entry for cn=ldapjs.

dn: cn=ldapjs, ou=users, o=myhost
objectClass: unixUser
cn: ldapjs
shell: /bin/bash
description: Created via ldapadd

But afterwards it shows the Operations error. I don't know why it still prints the matched DN: ou=users,o=myhost line, but it does not add the new entry when verifying with ldapsearch.

The ssh terminals with the ldap server and commands: Screenshot 2023-09-12 162450

The ldap server is started with root/sudo. I have tried this on both WSL Ubuntu and on a Raspberry Pi with the same error. I have asked this question on Stackoverflow but I suppose I should ask this directly.

jsumners commented 1 year ago

Please provide a minimal reproducible example. Doing so will help us diagnose your issue. It should be the bare minimum code needed to trigger the issue, and easily runnable without any changes or extra code.

You may use a GitHub repository to host the code if it is too much to fit in a code block (or two).


Aside from that, my best suggestion would be to step through your application with a debugger to determine what triggers the error.

Ketho commented 1 year ago

This is the example I've been using from the guide. It's mostly copied from there as I'm not sure how it all works. https://github.com/Ketho/ldapjs_example/blob/master/minimal_example.js

Stepping through the application with a debugger seems a bit daunting but I will try that.

Oh I forgot to include the user.ldif to the repository

jsumners commented 1 year ago

Please review the links and body of my previous message. Your example is relying on external resources and requires extra effort to use.

Ketho commented 1 year ago

I'll be honest, I don't know anything about LDAP, that's why I'm following the guide. I have literally no idea how to not rely on the external resources like child_process or any unneeded code from the guide. The /etc/passwd file is the default one from Ubuntu/Debian.

jsumners commented 1 year ago

LDAP knowledge really doesn't factor in to creating a minimal reproduction. You should supply the code, the code should reference a fixture (e.g. a passwd file with fake data), and invoke the error state with node index.js or npm start.

Ketho commented 1 year ago

Okay, when I get back home I will followup with a minimal reproduction

Ketho commented 1 year ago

In WSL I tried adding a few console logs since debugging with breakpoints didn't really work when invoking ldapadd. Then I noticed the req.dn param in server.add() is empty for me with both the minimal example and the /etc/passwd server example.

const ldap = require('ldapjs');
const server = ldap.createServer();

server.bind('cn=root', (req, res, next) => {
  res.end();
  return next();
});

server.add('ou=users, o=myhost', [], (req, res, next) => {
  console.log(req.dn); // this is an empty object for me
  res.end();
  return next();
});

server.listen(1389, '127.0.0.1', () => {
  console.log('example LDAP server up at: %s', server.url);
});

image

Is there something wrong with my setup, or did I remove too much code? I have added my install and test script to the example repository, it only uses a user.ldif and no passwd file. https://github.com/Ketho/ldapjs_example

jsumners commented 1 year ago

console.log(req.dn) should be console.log(req.dn.toString()).

Ketho commented 1 year ago

Hope you can see if I'm doing something wrong here.

// console.log(req);
AddRequest [LdapMessage] {
  log: { child: [Function (anonymous)] },
  connection: <ref *1> Socket {
    connecting: false,
    _hadError: false,
    _parent: null,
    _host: null,
    _closeAfterHandlingError: false,
    _readableState: ReadableState {
      objectMode: false,
      highWaterMark: 16384,
      buffer: BufferList { head: null, tail: null, length: 0 },
      length: 0,
      pipes: [],
      flowing: true,
      ended: false,
      endEmitted: false,
      reading: false,
      constructed: true,
      sync: false,
      needReadable: true,
      emittedReadable: false,
      readableListening: false,
      resumeScheduled: false,
      errorEmitted: false,
      emitClose: false,
      autoDestroy: true,
      destroyed: false,
      errored: null,
      closed: false,
      closeEmitted: false,
      defaultEncoding: 'utf8',
      awaitDrainWriters: null,
      multiAwaitDrain: false,
      readingMore: false,
      dataEmitted: true,
      decoder: null,
      encoding: null,
      [Symbol(kPaused)]: false
    },
    _events: [Object: null prototype] {
      end: [Array],
      timeout: [Function (anonymous)],
      error: [Function (anonymous)],
      close: [Function (anonymous)],
      data: [Function (anonymous)]
    },
    _eventsCount: 5,
    _maxListeners: undefined,
    _writableState: WritableState {
      objectMode: false,
      highWaterMark: 16384,
      finalCalled: false,
      needDrain: false,
      ending: false,
      ended: false,
      finished: false,
      destroyed: false,
      decodeStrings: false,
      defaultEncoding: 'utf8',
      length: 0,
      writing: false,
      corked: 0,
      sync: false,
      bufferProcessing: false,
      onwrite: [Function: bound onwrite],
      writecb: null,
      writelen: 0,
      afterWriteTickInfo: null,
      buffered: [],
      bufferedIndex: 0,
      allBuffers: true,
      allNoop: true,
      pendingcb: 0,
      constructed: true,
      prefinished: false,
      errorEmitted: false,
      emitClose: false,
      autoDestroy: true,
      errored: null,
      closed: false,
      closeEmitted: false,
      [Symbol(kOnFinished)]: []
    },
    allowHalfOpen: false,
    _sockname: null,
    _pendingData: null,
    _pendingEncoding: '',
    server: Server {
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      _connections: 1,
      _handle: [TCP],
      _usingWorkers: false,
      _workers: [],
      _unref: false,
      allowHalfOpen: false,
      pauseOnConnect: false,
      noDelay: false,
      keepAlive: false,
      keepAliveInitialDelay: 0,
      highWaterMark: 16384,
      log: [Object],
      ldap: [Object],
      _connectionKey: '4:127.0.0.1:1389',
      [Symbol(kCapture)]: false,
      [Symbol(async_id_symbol)]: 6
    },
    _server: Server {
      _events: [Object: null prototype],
      _eventsCount: 3,
      _maxListeners: undefined,
      _connections: 1,
      _handle: [TCP],
      _usingWorkers: false,
      _workers: [],
      _unref: false,
      allowHalfOpen: false,
      pauseOnConnect: false,
      noDelay: false,
      keepAlive: false,
      keepAliveInitialDelay: 0,
      highWaterMark: 16384,
      log: [Object],
      ldap: [Object],
      _connectionKey: '4:127.0.0.1:1389',
      [Symbol(kCapture)]: false,
      [Symbol(async_id_symbol)]: 6
    },
    _peername: { address: '127.0.0.1', family: 'IPv4', port: 44352 },
    ldap: {
      id: '127.0.0.1:44352',
      config: [Object],
      _bindDN: DN [LdapDn] {},
      bindDN: [Getter/Setter]
    },
    parser: Parser {
      _events: [Object: null prototype],
      _eventsCount: 2,
      _maxListeners: undefined,
      buffer: null,
      log: [Object],
      [Symbol(kCapture)]: false
    },
    [Symbol(async_id_symbol)]: 10,
    [Symbol(kHandle)]: TCP {
      reading: true,
      onconnection: null,
      [Symbol(owner_symbol)]: [Circular *1]
    },
    [Symbol(lastWriteQueueSize)]: 0,
    [Symbol(timeout)]: null,
    [Symbol(kBuffer)]: null,
    [Symbol(kBufferCb)]: null,
    [Symbol(kBufferGen)]: null,
    [Symbol(kCapture)]: false,
    [Symbol(kSetNoDelay)]: false,
    [Symbol(kSetKeepAlive)]: false,
    [Symbol(kSetKeepAliveInitialDelay)]: 0,
    [Symbol(kBytesRead)]: 0,
    [Symbol(kBytesWritten)]: 0
  },
  logId: '127.0.0.1:44352::2',
  startTime: 1694609333546,
  suffix: DN [LdapDn] {}
}
tfrancois commented 8 months ago

Hi @Ketho - did you ever find the source of the issue of your "Operations Error'? I'm now getting the same issue when attempting to add an attribute on a MS Windows/LDAP server. When I perform the same operation on an Samba LDAP server no such error exists. Let me know - thanks.

Ketho commented 8 months ago

I did not find the source of the error but your issue does sound like it would warrant making a separate ticket. In the end I resorted to testing with python ldap3 to learn about LDAP.

tfrancois commented 8 months ago

In case it might help someone - I did end up solving the issue! And this is important. For AD Windows servers, making any changes to schema using LDAP require the following conditions to be met:

I kept getting Operations Error message until I fixed replication on my server and once I ran the command above with no errors, POOF! like magic the LDAP operations I was sending (unchanged) worked perfectly. Too bad the error messages returned by LDAPjs don't report this correctly but that was the source of my issues. Hope that helps someone.

byteAr commented 6 months ago

Hello @tfrancois , I have the problem that I cannot change the password of a user using the administrator credentials when making the request with modify(), the nodejs project is on a different server than the one running activeDirectory, yes I request to search for a user by name, the operation is carried out successfully, but if I try to modify it, the password does not allow me to do so.