#50865 In lib389 search for vendorVersion is requested after BIND
Closed: wontfix 3 years ago by spichugi. Opened 4 years ago by vashirov.

Issue Description

After b74780e I see the following search in the logs:

[29/Jan/2020:10:51:09.341654365 +0000] conn=1 op=14 SRCH base="" scope=0 filter="(objectClass=*)" attrs="vendorVersion"

In a case, when a password policy is active, password was reset by DM and the user tries to connect to change the password, the operation fails. Because right after the BIND, a SRCH is sent that gets denied:

[29/Jan/2020:10:51:09.641418797 +0000] conn=2 op=1 UNPROCESSED OPERATION - need new password

Package Version and Platform

1.4.3.1+

Steps to reproduce

https://pagure.io/389-ds-base/issue/raw/files/8d7f6ca6b23d45120b283c64714958942a877fc4d9e6cf74d98546d2d67ddf99-test_ds50864.py

Actual results

[29/Jan/2020:14:15:07.219147220 +0000] conn=2 op=0 BIND dn="uid=tuser,ou=people,dc=example,dc=com" method=128 version=3
[29/Jan/2020:14:15:07.268878819 +0000] conn=2 op=0 RESULT err=0 tag=97 nentries=0 etime=0.049961260 dn="uid=tuser,ou=people,dc=example,dc=com"
[29/Jan/2020:14:15:07.269644119 +0000] conn=2 op=1 UNPROCESSED OPERATION - need new password
[29/Jan/2020:14:15:07.269683274 +0000] conn=2 op=1 RESULT err=53 tag=101 nentries=0 etime=0.000171006

Expected results

[29/Jan/2020:14:15:33.977424219 +0000] conn=2 op=0 BIND dn="uid=tuser,ou=people,dc=example,dc=com" method=128 version=3
[29/Jan/2020:14:15:34.003413874 +0000] conn=2 op=0 RESULT err=0 tag=97 nentries=0 etime=0.026098997 dn="uid=tuser,ou=people,dc=example,dc=com"
[29/Jan/2020:14:15:34.005259973 +0000] conn=1 op=12 MOD dn="uid=tuser,ou=people,dc=example,dc=com"
[29/Jan/2020:14:15:34.031093947 +0000] conn=1 op=12 RESULT err=0 tag=103 nentries=0 etime=0.026212070

Well that's awkward. We need the version to know what functions we can actually call based on the remote version ... I'm not sure what's the right answer here ....

Metadata Update from @firstyear:
- Custom field origin adjusted to None
- Custom field reviewstatus adjusted to None

4 years ago

Does it really have to be on every BIND? There are only a handful places in the lib389 code where we need to care about the DS version, which are handled by ds_is_older()/ds_is_newer().

Metadata Update from @mreynolds:
- Issue set to the milestone: 1.4.3

4 years ago

No it doesn't have to be every bind - the current design should only call ds_is_older/newer() when needed. So in this case ... maybe it's needed? I suspect it'll be in Account when it's trying to determine if we are on a 1.4.x to have new schema and what's possible.

Okay so looking at your reproducer, it's pretty clear what's happening.

As you build the second UserAccount from:

    # Bind should be successful
    conn = user.bind(password)
    UserAccount(topo.standalone, f'uid=tuser,ou=people,{DEFAULT_SUFFIX}').set('userPassword', 'my_new_password')

This has to pass through init(), which is calling:

        if ds_is_older('1.3.7', instance=instance):
            self._create_objectclasses.append('inetUser')
        else:
            self._create_objectclasses.append('nsMemberOf')
        if not ds_is_older('1.4.0', instance=instance):
            self._create_objectclasses.append('nsAccount')
        user_compare_exclude = [
            'nsUniqueId',
            'modifyTimestamp',
            'createTimestamp',
            'entrydn'
        ]

This is because we don't know how you plan to use the UserAccount type, if you are making a new object or not at this point - it's just the nature of how it works. So this means you'll always call the ds_is_older etc.

I believe the way the CLI tools avoid this is by calling Account instead, because account as the base class can do the password reset regardless of the actual object class type (which is kind of the point ....). We use UserAccount for "specific" tasks to those types like creation and modification that requires the awareness of the class structure, but Account works on "anything" that needs reset or locking.

So I think that this doesn't affect the CLI tools/callers when they need to reset the password as it avoids the ds_is_older call.

It doesn't work with plain Account too:

  /workspace/ds/test_reproducer.py(40)test_change_pw()
-> assert user.bind(password)
  /workspace/ds/src/lib389/lib389/idm/account.py(207)bind()
-> inst_clone.open(*args, **kwargs)
  /workspace/ds/src/lib389/lib389/__init__.py(1089)open()
-> self.__add_brookers__()
  /workspace/ds/src/lib389/lib389/__init__.py(355)__add_brookers__()
-> self.monitorldbm = MonitorLDBM(self)
> /workspace/ds/src/lib389/lib389/monitor.py(123)__init__()
-> if not ds_is_older("1.4.0", instance=instance):
(Pdb) 

As you can see, we hit ds_is_older() anyway, because at the end of open() we call __add_brookers__() which calls MonitorLDBM().

What I don't understand is why we do ldapsearch for vendorVersion before we look it up in defaults.inf?

Because we can't guarantee we are on a machine with a defaults.inf - imagine a remote client or admin workstation with lib389 only, and no 389-ds-base? Or a situation where you have a 389-ds-base but the version isn't the same? So we have to look it up from the rootdse, not the defaults.inf because it may not be there or may be incorrect.

The whole add_brookers method is awful and I wish we could remove it, because it shouldn't be in that path at all :|

389-ds-base is moving from Pagure to Github. This means that new issues and pull requests
will be accepted only in 389-ds-base's github repository.

This issue has been cloned to Github and is available here:
- https://github.com/389ds/389-ds-base/issues/3918

If you want to receive further updates on the issue, please navigate to the github issue
and click on subscribe button.

Thank you for understanding. We apologize for all inconvenience.

Metadata Update from @spichugi:
- Issue close_status updated to: wontfix
- Issue status updated to: Closed (was: Open)

3 years ago

Login to comment on this ticket.

Metadata