From c11c22a184c67fa8abcc8d315175f5b25f6cbe1a Mon Sep 17 00:00:00 2001 From: Sami El-Daher Date: Mon, 30 Oct 2023 15:13:41 +0000 Subject: [PATCH 1/5] Add integration test for PasswordPolicyControl --- test-integration/client/issues.test.js | 74 ++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/test-integration/client/issues.test.js b/test-integration/client/issues.test.js index ab10d113..83181b2d 100644 --- a/test-integration/client/issues.test.js +++ b/test-integration/client/issues.test.js @@ -96,3 +96,77 @@ tap.test('can access large groups (issue #582)', t => { }) }) }) + +tap.test('can use password policy response', t => { + const client = ldapjs.createClient({ url: baseURL }) + + client.bind('cn=admin,dc=planetexpress,dc=com', 'GoodNewsEveryone', (err) => { + t.error(err) + + const passwordChangeAttribute = new ldapjs.Attribute({ type: 'userPassword', values: 'bender2' }) + const passwordChange = new ldapjs.Change({ operation: 'replace', modification: passwordChangeAttribute }) + + const targetDN = 'cn=Bender Bending Rodríguez,ou=people,dc=planetexpress,dc=com' + client.modify(targetDN, passwordChange, (err, res) => { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + + client.unbind(err => { + t.error(err) + + const client2 = ldapjs.createClient({ url: baseURL }) + const control = new ldapjs.PasswordPolicyControl() + + client2.bind(targetDN, 'bender2', control, (err, res) => { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + + let error = null + res.controls.forEach(control => { + if (control.type === ldapjs.PasswordPolicyControl.OID) { + error = control.value.error + } + }) + if (error) { + t.equal(error, 2) + } else { + t.fail('Expected error to be set') + } + + const passwordChangeAttribute2 = new ldapjs.Attribute({ type: 'userPassword', values: 'bender' }) + const passwordChange2 = new ldapjs.Change({ operation: 'replace', modification: passwordChangeAttribute2 }) + client2.modify(targetDN, passwordChange2, (err, res) => { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + + client2.unbind(err => { + t.error(err) + + const client3 = ldapjs.createClient({ url: baseURL }) + client3.bind(targetDN, 'bender', control, (err, res) => { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + + let timeBeforeExpiration = null + res.controls.forEach(control => { + if (control.type === ldapjs.PasswordPolicyControl.OID) { + timeBeforeExpiration = control.value.timeBeforeExpiration + } + }) + if (timeBeforeExpiration === null) { + t.fail('Expected timeBeforeExpiration to be set') + } + + client3.unbind(t.end) + }) + }) + }) + }) + }) + }) + }) +}) From 136b5083380d24fc378e3f222f3ab99fafb63dba Mon Sep 17 00:00:00 2001 From: Sami El-Daher Date: Mon, 30 Oct 2023 15:13:41 +0000 Subject: [PATCH 2/5] Split test case into separate file - Bump docker image tag --- docker-compose.yml | 2 +- test-integration/client/issue-946.test.js | 87 +++++++++++++++++++++++ 2 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 test-integration/client/issue-946.test.js diff --git a/docker-compose.yml b/docker-compose.yml index 0a7e335a..19c4b5f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,6 +1,6 @@ services: openldap: - image: ghcr.io/ldapjs/docker-test-openldap/openldap:2023-08-15 + image: ghcr.io/ldapjs/docker-test-openldap/openldap:2023-10-30 ports: - 389:389 - 636:636 diff --git a/test-integration/client/issue-946.test.js b/test-integration/client/issue-946.test.js new file mode 100644 index 00000000..230c0d6f --- /dev/null +++ b/test-integration/client/issue-946.test.js @@ -0,0 +1,87 @@ +'use strict' + +const tap = require('tap') +const ldapjs = require('../../lib') + +const SCHEME = process.env.SCHEME || 'ldap' +const HOST = process.env.HOST || '127.0.0.1' +const PORT = process.env.PORT || 389 +const baseURL = `${SCHEME}://${HOST}:${PORT}` + +tap.test('can use password policy response', t => { + const client = ldapjs.createClient({ url: baseURL }) + const targetDN = 'cn=Bender Bending Rodríguez,ou=people,dc=planetexpress,dc=com' + + client.bind('cn=admin,dc=planetexpress,dc=com', 'GoodNewsEveryone', (err, res) => { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + + const newPassword = 'bender2' + changePassword(client, newPassword, () => { + client.unbind() + bindNewClient(newPassword, { error: 2 }, (client) => { + const newPassword = 'bender' + changePassword(client, newPassword, () => { + client.unbind() + bindNewClient(newPassword, { timeBeforeExpiration: 1000 }, (client) => { + client.unbind(t.end) + }) + }) + }) + }) + }) + + function bindNewClient (pwd, expected, callback) { + const client = ldapjs.createClient({ url: baseURL }) + const control = new ldapjs.PasswordPolicyControl() + + client.bind(targetDN, pwd, control, (err, res) => { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + + let error = null + let timeBeforeExpiration = null + let graceAuthNsRemaining = null + + res.controls.forEach(control => { + if (control.type === ldapjs.PasswordPolicyControl.OID) { + error = control.value.error ?? error + timeBeforeExpiration = control.value.timeBeforeExpiration ?? timeBeforeExpiration + graceAuthNsRemaining = control.value.graceAuthNsRemaining ?? graceAuthNsRemaining + } + }) + + if (expected.error !== undefined) { + t.equal(error, expected.error) + } + if (expected.timeBeforeExpiration !== undefined) { + t.equal(timeBeforeExpiration, expected.timeBeforeExpiration) + } + if (expected.graceAuthNsRemaining !== undefined) { + t.equal(graceAuthNsRemaining, expected.graceAuthNsRemaining) + } + + callback(client) + }) + } + + function changePassword (client, newPwd, callback) { + const change = new ldapjs.Change({ + operation: 'replace', + modification: new ldapjs.Attribute({ + type: 'userPassword', + values: newPwd + }) + }) + + client.modify(targetDN, change, (err, res) => { + t.error(err) + t.ok(res) + t.equal(res.status, 0) + + callback() + }) + } +}) From bace45024d7737bbd4a5eb82d7d32c9d1477e1a8 Mon Sep 17 00:00:00 2001 From: Sami El-Daher Date: Tue, 31 Oct 2023 14:40:14 +0000 Subject: [PATCH 3/5] Remove test case from top level test file --- test-integration/client/issues.test.js | 74 -------------------------- 1 file changed, 74 deletions(-) diff --git a/test-integration/client/issues.test.js b/test-integration/client/issues.test.js index 83181b2d..ab10d113 100644 --- a/test-integration/client/issues.test.js +++ b/test-integration/client/issues.test.js @@ -96,77 +96,3 @@ tap.test('can access large groups (issue #582)', t => { }) }) }) - -tap.test('can use password policy response', t => { - const client = ldapjs.createClient({ url: baseURL }) - - client.bind('cn=admin,dc=planetexpress,dc=com', 'GoodNewsEveryone', (err) => { - t.error(err) - - const passwordChangeAttribute = new ldapjs.Attribute({ type: 'userPassword', values: 'bender2' }) - const passwordChange = new ldapjs.Change({ operation: 'replace', modification: passwordChangeAttribute }) - - const targetDN = 'cn=Bender Bending Rodríguez,ou=people,dc=planetexpress,dc=com' - client.modify(targetDN, passwordChange, (err, res) => { - t.error(err) - t.ok(res) - t.equal(res.status, 0) - - client.unbind(err => { - t.error(err) - - const client2 = ldapjs.createClient({ url: baseURL }) - const control = new ldapjs.PasswordPolicyControl() - - client2.bind(targetDN, 'bender2', control, (err, res) => { - t.error(err) - t.ok(res) - t.equal(res.status, 0) - - let error = null - res.controls.forEach(control => { - if (control.type === ldapjs.PasswordPolicyControl.OID) { - error = control.value.error - } - }) - if (error) { - t.equal(error, 2) - } else { - t.fail('Expected error to be set') - } - - const passwordChangeAttribute2 = new ldapjs.Attribute({ type: 'userPassword', values: 'bender' }) - const passwordChange2 = new ldapjs.Change({ operation: 'replace', modification: passwordChangeAttribute2 }) - client2.modify(targetDN, passwordChange2, (err, res) => { - t.error(err) - t.ok(res) - t.equal(res.status, 0) - - client2.unbind(err => { - t.error(err) - - const client3 = ldapjs.createClient({ url: baseURL }) - client3.bind(targetDN, 'bender', control, (err, res) => { - t.error(err) - t.ok(res) - t.equal(res.status, 0) - - let timeBeforeExpiration = null - res.controls.forEach(control => { - if (control.type === ldapjs.PasswordPolicyControl.OID) { - timeBeforeExpiration = control.value.timeBeforeExpiration - } - }) - if (timeBeforeExpiration === null) { - t.fail('Expected timeBeforeExpiration to be set') - } - - client3.unbind(t.end) - }) - }) - }) - }) - }) - }) - }) -}) From 1c3af9bdedfc2276b43af6c74622326cd331a710 Mon Sep 17 00:00:00 2001 From: Sami El-Daher Date: Fri, 3 Nov 2023 13:52:05 +0000 Subject: [PATCH 4/5] Upgrade dependencies to include PasswordPolicyControl --- package.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 883ad0dc..2faa9cfd 100644 --- a/package.json +++ b/package.json @@ -14,10 +14,10 @@ "@ldapjs/asn1": "^2.0.0", "@ldapjs/attribute": "^1.0.0", "@ldapjs/change": "^1.0.0", - "@ldapjs/controls": "^2.0.0", + "@ldapjs/controls": "^2.1.0", "@ldapjs/dn": "^1.1.0", "@ldapjs/filter": "^2.1.1", - "@ldapjs/messages": "^1.2.1", + "@ldapjs/messages": "^1.3.0", "@ldapjs/protocol": "^1.2.1", "abstract-logging": "^2.0.1", "assert-plus": "^1.0.0", From 8442c3dafbcef66489cd53535317803b1993e469 Mon Sep 17 00:00:00 2001 From: Sami El-Daher Date: Fri, 10 Nov 2023 10:10:10 +0000 Subject: [PATCH 5/5] Upgrade openldap docker image in test workflow --- .github/workflows/integration.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 2a3a14ea..71e1bf53 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -20,7 +20,7 @@ jobs: services: openldap: - image: ghcr.io/ldapjs/docker-test-openldap/openldap:2023-08-15 + image: ghcr.io/ldapjs/docker-test-openldap/openldap:2023-10-30 ports: - 389:389 - 636:636