Hypertopic / AAAforREST

An HTTP reverse proxy to bring authentication, authorization and accounting to RESTful applications
GNU Affero General Public License v3.0
6 stars 5 forks source link

Interacting with a multiple subdomain LDAP #58

Closed christophe-lejeune closed 2 years ago

christophe-lejeune commented 2 years ago

Thank you very much for the great work done on AAAforREST !

I would like to use it with my university's LDAP. My first attempt was successful. That'a already great !

However, I realized that my LDAP have "subdomains" for different types of students. In the example provided in conf/config.yml, we would have two different search bases, one with dc=example,dc=com and the other with dc=master,dc=example,dc=com. Each configuration works if it is used alone. But I fail to use it altogether.

As you can see, the search bases have dc=example,dc=com in common. Unfortunately, queries on dc=example,dc=com gives no answer concerning dc=master,dc=example,dc=com. Do you think it would be possible to generate "recursive" queries to this LDAP ? Or should we consider these two search bases as two different LDAP, as in first version of AAAforREST ?

I would be happy to conduct tests under your direction.

Again many thanks and best wishes !

benel commented 2 years ago

The scope is a an option of ldap.js search. By default, is is set to base but could be set to sub. It is called from our Ldap module in the method searchAndBind. The new scope could be hard coded, or (preferably) changed in the config file.

benel commented 2 years ago

I suggest to have the scope hard coded with sub BUT also that administrators would use the greatest common search path (e.g. : ou=people,dc=acme,dc=edu for classic AD-like directory and dc=acme,dc=edu for a directory with multiple subtrees).

benel commented 2 years ago

searchAndBind has two steps: search and bind.

search can be applied recursively from any broader node. bind must be applied to the exact node.

@christophe-lejeune I will share a small node program so that you can check that your LDAP server is able to generate on first step the input needed in the second step.

benel commented 2 years ago

Testable on any computer that can connect to your LDAP server and on which you can have Node.js installed.

In a new folder.

Copy-paste in a new file package.json:

{
  "name": "ldaptest",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "ldapjs": "^2.3.1",
    "minimist": "^1.2.5"
  }
}

Copy-paste in a new file named index.js:

const LDAP = require("ldapjs");
const {filter, url, searchBase} = require('minimist')(process.argv.slice(2));

let close = (ldap) => {
  if (ldap._socket) {
    ldap._socket.end()
  }
}

let ldap = LDAP.createClient({url});

ldap.search(searchBase, {scope: 'sub', filter, attributes: ['dn']}, (tcpError, res) => {
    if (tcpError) {
        close(ldap);
        console.log('TCP error');
    } else {
        let dn;
        res.on('searchEntry', (entry) => {
          dn = entry.object.dn;
        });
        res.on('end', () => {
          close(ldap);
          console.log({dn});
        });
  }
});

Install dependencies:

npm install

Launch the program to test your LDAP server ability to search for the complete path ("distinguished name") of a user from his/her ID :

node index.js --url ldap://ldap.acme.fr --searchBase dc=acme,dc=fr --filter uid=benel  

You'll get for example:

uid=benel,ou=people,dc=acme,dc=fr

Test it on IDs of people with different status.

christophe-lejeune commented 2 years ago

Dear @benel

Thank you very much for this node program, super easy to deploy within seconds on a test machine.

The issues of this test seems to confirm that the my university's LDAP accepts "recursive" requests

Indeed, it have results with id belonging to different status:

{ dn: 'uid=lejeune,ou=people,dc=acme,dc=fr' }
{ dn: 'uid=benel,ou=people,dc=master,dc=acme,dc=fr' }

And, when the id is unknown :

{ dn: undefined }

Are these results sufficient to make a try with scope: "sub" or does the two steps (search and bind) require additional developments prior to make such tests.

Again many thank for your helps in deploying/adapting AAAforREST for my needs !

benel commented 2 years ago

Are these results sufficient to make a try with scope: "sub" or does the two steps (search and bind) require additional developments prior to make such tests.

Additional (small)) developments are necessary to inject the resulted DN into the bind.

benel commented 2 years ago

And here is a new version that searches AND binds:

const LDAP = require("ldapjs");
const {filter, url, searchBase, password} = require('minimist')(process.argv.slice(2));

let close = (ldap) => {
  if (ldap._socket) {
    ldap._socket.end()
  }
}

let ldap = LDAP.createClient({url});

ldap.search(searchBase, {scope: 'sub', filter, attributes: ['dn']}, (tcpError, res) => {
    if (tcpError) {
        close(ldap);
        console.log('TCP error');
    } else {
        let dn;
        res.on('searchEntry', (entry) => {
          dn = entry.object.dn;
        });
        res.on('end', () => {
          console.log({dn});
          ldap.bind(dn, password, (badPassword) => {
            close(ldap);
            console.log({logged: !badPassword});
          });
        });
      }
});
node index.js --url ldap://ldap.acme.fr --searchBase dc=acme,dc=fr --filter uid=benel  --password "p8ssW0rd"
{ dn: 'uid=benel,ou=people,dc=acme,dc=fr' }
{ logged: true }
christophe-lejeune commented 2 years ago

I confirm the messages are different when the id and the password matches { logged: true }, when the password does not match the id { logged: false } and when the id does not belong to the ldap { dn: undefined }.

In the latter case, an exception is raised, but if does not invalidate the test.

benel commented 2 years ago

I published a temporary container image named "aaaforrest:dev". If you have time to test it, please tell me if it works in your settings.

For now, the existing tests do not pass . Sorry for that.

benel commented 2 years ago

A side effect has just been fixed. Tests are passing again. The new container image (aaaforrest:dev) can be pulled for testing.

christophe-lejeune commented 2 years ago

This temporary container (with the fixed side effect) works for me ! Thanks to this code and configuration, users from different nodes can log in !

Thank you very much and congratulations for this great work !

benel commented 2 years ago

Great! The new container image is now published on DockerHub as aaaforrest (latest)!