node-ldapjs icon indicating copy to clipboard operation
node-ldapjs copied to clipboard

bind response - password expiring overrides force password change

Open ShlomitGilo1 opened this issue 3 years ago • 22 comments

Hi! I'm implementing the feature of forcing password change on first login, when a user is added or when admin is changing user's password. I set ds-cfg-force-change-on-add and ds-cfg-force-change-on-reset to true, and following the spec, which defines:

10. Password Change After Reset This policy forces the user to select a new password on first bind or after password reset. After bind operation succeed with authentication, the server should check if the password change after reset policy is on and this is the first time logon. If so, the server should send bindResponse with the resultCode: LDAP_SUCCESS, and should include the password expired control in the controls field of the bindResponse message: controlType: 2.16.840.1.113730.3.4.4, controlValue: an octet string: "0", criticality: false

Indeed, when I call Client.bind, I get return value LDAP_SUCCESS, and the controls field as defined in the spec.

BUT - when I call Client.bind when the user is within its password expiration warning interval, I get the controls field only with the password expiring controlType (2.16.840.1.113730.3.4.5). I would expect to get both controlType elements (controls is an array), but 2.16.840.1.113730.3.4.4 is not there.

This is a major problem because if the controlType 2.16.840.1.113730.3.4.4 is not there - the user will be able to login although he supposed to be not.

What am I missing here?

Thanks.

ShlomitGilo1 avatar Nov 17 '21 10:11 ShlomitGilo1

How does this relate to the ldapjs module?

jsumners avatar Nov 17 '21 12:11 jsumners

because I'm using ldapjs package for all LDAP related code

ShlomitGilo1 avatar Nov 17 '21 12:11 ShlomitGilo1

because I'm using ldapjs package for all LDAP related code

Please provide a minimal reproduction. It is not at all clear what issue you are presenting.

jsumners avatar Nov 17 '21 13:11 jsumners

ok, I'll try to clarify. I writing a service in node, and using ldapjs package.

I'm using Client.bind() method of the package. The signature of the method is: bind(dn: string, password: string, callback: CallBack): void;

The callback returns error in case of bind failure and 'result' with additional info in case of bind success.

When LDAP feature 'force password change' is set, when user should reset his password, his bind 'result' field indicates that by setting a field in 'result', called 'controls', with a 'controlType' attribute set to 2.16.840.1.113730.3.4.4.

The problem is, that when a user should change his password and he's also in the password expiring warning interval, the bind 'result' field indicates only the password expiring warning (by setting 'controlType' to 2.16.840.1.113730.3.4.5), and the force password change indication is not there.

It's a major problem, because if I don't get the 'controlType' of the force password reset, I don't know I should block this user login.

ShlomitGilo1 avatar Nov 17 '21 13:11 ShlomitGilo1

The client receives responses from the server and passes them along to your code. Unless you can provide a minimal reproduction to show there is an error in the ldapjs client code, this is an issue with your server.

jsumners avatar Nov 17 '21 13:11 jsumners

You are right. It's the behavior of the server, and not an error in ldapjs. However, I got this answer in StackOverflow: "The Netscape controls are very old and predate the OpenDJ password policy work. They are just there to ensure some form of compatibility with very old applications. New applications should send the Password Policy Request Control (1.3.6.1.4.1.42.2.27.8.5.1) and will receive the proper PwdPolicy control response."

The purpose here is probably to use the 'controls' argument in ldapjs bind method, to explicitly request the password policy controls of the user. I failed to find the proper way to provide this argument. I tried:

Client.bind(<dn>, <password>, {type: "1.3.6.1.4.1.42.2.27.8.5.1", criticality: false}, <callback>)

but it throws the exception: controls must be [Control]

What is the right syntax to use?

ShlomitGilo1 avatar Nov 22 '21 07:11 ShlomitGilo1

Hi, Can someone please answer my last syntax question above....?

ShlomitGilo1 avatar Nov 28 '21 13:11 ShlomitGilo1

What does the documentation state?

jsumners avatar Nov 28 '21 13:11 jsumners

image image

ShlomitGilo1 avatar Nov 28 '21 13:11 ShlomitGilo1

So does your example provide an array of controls?

jsumners avatar Nov 28 '21 13:11 jsumners

It throws the exception also when I use an array: Client.bind(<dn>, <password>, [{type: "1.3.6.1.4.1.42.2.27.8.5.1", criticality: false}], <callback>)

According to the types file, it can be either an array or not image

ShlomitGilo1 avatar Nov 28 '21 13:11 ShlomitGilo1

This project does not provide any "types file". Please review the tests for sample usages. For example:

https://github.com/ldapjs/node-ldapjs/blob/1f85db9a00426abda117abace7dd3b283e08fc25/test/client.test.js#L581-L591

jsumners avatar Nov 28 '21 13:11 jsumners

What do you mean This project does not provide any "types file"? npm install ldapjs is not enough. It gives the warning:

Could not find a declaration file for module 'ldapjs'. 'c:/develop/ldapUmsSearchTest/node_modules/ldapjs/lib/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/ldapjs` if it exists or add a new declaration (.d.ts) file containing `declare module 'ldapjs';`ts(7016)

ShlomitGilo1 avatar Nov 28 '21 13:11 ShlomitGilo1

I don't know how to be any clearer: ldapjs does not provide any types file or package.

jsumners avatar Nov 28 '21 14:11 jsumners

I don't understand. I'm writing ts code, and using ldapjs package. As defined in your README, image

so that's what I do. I install the package. Since I'm writing in ts, I need types.

ShlomitGilo1 avatar Nov 28 '21 14:11 ShlomitGilo1

This project is a JavaScript project. If you use it with TypeScript then anything pertaining to TypeScript is up to you.

As for the original question, I think we have covered the usage of controls through reading the documentation and unit tests. Is this the case?

jsumners avatar Nov 28 '21 15:11 jsumners

types are provided by @types/ldapjs. We cannot confirm that they are accurate since we did not write them.

Follow the directions in the error message:

Try `npm i --save-dev @types/ldapjs` if it exists or add a new declaration (.d.ts) file containing `declare module 'ldapjs';`

UziTech avatar Nov 28 '21 16:11 UziTech

If you have an issue with the types in @types/ldapjs you can create an issue on the DefinitelyTyped repo.

UziTech avatar Nov 28 '21 16:11 UziTech

Hi, I think this thread has revolted on Typescript when it's not part of the issue at all, only that is not being helpful restricting the type, so it allowed @ShlomitGilo1 to make a mistake.

The actual issue seems to be that @ShlomitGilo1 is using a POJO (plain old JS object), when the ldapjs library requires an actual instance of the Control class.

You can see this in:

  • In the docs

    Note that all client APIs can optionally take an array of Control objects.

    There Control refers to the ldap.Control class, although is not very explicit. I think docs could be improved.

  • In the sample that @jsumners provided: link There you can see it's using new ldap.Control({ type: ... })

pmoleri avatar Dec 07 '21 23:12 pmoleri

I think docs could be improved.

Yes. The docs need a lot of attention.

jsumners avatar Dec 08 '21 03:12 jsumners

ok, I got over the ts issue, and now I'm able to use the controls argument. Great. But I probably don't understand how to use it. All I want is, as documented, "to request information about the current password policy information for a user entry" (https://docs.oracle.com/cd/E19476-01/821-0506/searching-using-controls.html). I want to get to a situation where I receive in the response for bind/search requests, both controls of 2.16.840.1.113730.3.4.4 and 2.16.840.1.113730.3.4.5.

BUT when I set the controls argument in the request to: {type: "1.3.6.1.4.1.42.2.27.8.5.1", criticality: true}

for providing it in the search request I get the exception: UnavailableCriticalExtensionError: The search request cannot be processed because it contains a critical control with OID 1.3.6.1.4.1.42.2.27.8.5.1 that is not supported by the Directory Server for this type of operation

and for providing it in the bind request I get back the same value I sent in the request: "controls":[{"type":"1.3.6.1.4.1.42.2.27.8.5.1","criticality":false,"value":{"type":"Buffer","data":[48,3,129,1,2]}}]

What am I missing here?? how can I get 2.16.840.1.113730.3.4.4 and 2.16.840.1.113730.3.4.5 in the response?

ShlomitGilo1 avatar Dec 13 '21 12:12 ShlomitGilo1

Your server told you: OID 1.3.6.1.4.1.42.2.27.8.5.1 that is not supported by the Directory Server for this type of operation.

jsumners avatar Dec 13 '21 13:12 jsumners

👋

On February 22, 2023, we released version 3 of this library. As a result, we are closing this issue/pull request.

Please see issue #839 for more information, including how to proceed if you feel this closure is in error.

jsumners avatar Feb 22 '23 19:02 jsumners