Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LDAP: fix inGroup for memberUid type of group memberships #24402

Merged
merged 6 commits into from
Dec 15, 2020

Conversation

blizzz
Copy link
Member

@blizzz blizzz commented Nov 26, 2020

fixes #24252

basically, the code that resolves the rdns to full DNs returned the whole records, as @Hexasoft observed, but the following code requires a flat list of DNs.

@Hexasoft @steffen-moser can you confirm it fixes the issue for you?

@tofuSCHNITZEL you are informed because it is relevant for zimbramailforwardingaddress types, I would be surprised if it breaks anything though.

It set the label to "developing", because i want to check one, two things tomorrow.

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
@blizzz blizzz added this to the Nextcloud 21 milestone Nov 26, 2020
@blizzz blizzz mentioned this pull request Nov 26, 2020
@steffen-moser
Copy link

@blizzz - I think you also need to consider

return in_array($userDN, $this->cachedGroupMembers[$gid]);
as the list there is also not flat according to my debug log.

I've not checked whether

$isInGroup = in_array($userDN, $members, true);
needs fixing because I did not run into this if clause on any of our Nextcloud servers.

@tofuSCHNITZEL
Copy link
Contributor

I just ran a few tests and you change makes total sense... now $members really contains dns so the in_array check works.
But there are problems if this is tripped

if ($bytes >= 9000000) {

because then $dns is already filled but ins this form:

Array
(
    [0] => Array
        (
            [entryuuid] => Array
                (
                    [0] => c2a1b704-4463-1038-9b2b-b7afed259854
                )

            [dn] => Array
                (
                    [0] => uid=testaccount,ou=people,dc=email,dc=at
                )

            [uid] => Array
                (
                    [0] => testaccount
                )

            [displayname] => Array
                (
                    [0] => testaccount
                )

        )

    [1] => Array
        (
            [entryuuid] => Array
                (
                    [0] => 77dd1308-5857-1030-8796-392f61712f0b
                )

            [dn] => Array
                (
                    [0] => uid=user.name,ou=people,dc=email,dc=at
                )

            [uid] => Array
                (
                    [0] => user.name
                )

            [zimbramailalias] => Array
                (
                    [0] => user@email.at
                )

            [displayname] => Array
                (
                    [0] => user
                )

        )

)

and now the array_reduce part might or might not be run (depending if there are filterParts left)
if its run then using $dns as initial with the array_reduce does not produces the desired output.
if its not run then we have nonsense in $members like before this fix

TLDR:
after over an hour of playing around with different scenarios I think I found the solution that works with all cases.

I committed my changes to this branch... I hope this is okay? (and the proper way?)

I'm also thinking, that a foreach for cleaning up the array would be better to understand than the array_reduce.

we could replace:

				// now we cleanup the users array to get only dns
				$dns = array_reduce($users, function (array $carry, array $record) {
					if (!in_array($carry, $record['dn'][0])) {
						$carry[$record['dn'][0]] = 1;
					}
					return $carry;
				}, []);
				$members = array_keys($dns);

with a simple:

			// now create a list of dns
			$members = [];
			foreach ($users as $user){
				$dn = $user['dn'][0];
				if (!in_array($dn, $members))
					$members[] = $dn;
			}

also should we could (should?) move:

if (!is_array($members) || count($members) === 0) {
$this->access->connection->writeToCache($cacheKey, false);
return false;

after the switch statement

also added some additional comments and renamed some vars to make it intuitive whats in them

Signed-off-by: Tobias Perschon <tobias@perschon.at>
@blizzz
Copy link
Member Author

blizzz commented Nov 27, 2020

as the list there is also not flat according to my debug log.

is it possible that this was old information, perhaps pulled it from caching? Since it really only is assigned the value of $members. Also not that cachedGroupMembers is not a plain array, but an instance of CappedMemoryCache that behaves like one.

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
@blizzz
Copy link
Member Author

blizzz commented Nov 27, 2020

@tofuSCHNITZEL good catch with the loop! Also thanks for the commit, that's appreciated. I'd build upon it and turn the last array_reduce to an unsexy, but less hungry and more performant foreach (like you suggest). I'd still work with having the DN as index, as it saves in_arrays and prevents potential duplicates.

Moving the count check down makes sense as well. And dropping the is_array check with it since it is guaranteed by the return type.

- the type check is not necessary anymore for the return type of
  _groupMembers()

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
@tofuSCHNITZEL
Copy link
Contributor

great ;) should / will this fix be backported to 19 or even earlier? I guess sind its not a huge change we should/could do it. the structure of the file is a bit different pre v20 because of my changes to it for the zimbra support so I could port the changes to the v19 GroupLDAP file.

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
@blizzz
Copy link
Member Author

blizzz commented Nov 27, 2020

…and added unit tests.

great ;) should / will this fix be backported to 19 or even earlier? I guess sind its not a huge change we should/could do it. the structure of the file is a bit different pre v20 because of my changes to it for the zimbra support so I could port the changes to the v19 GroupLDAP file.

Yes, it is a bug fix, and should be brought back down to 18. If we need to touch a bit there, so be it :)

Signed-off-by: Arthur Schiwon <blizzz@arthur-schiwon.de>
@blizzz blizzz added 3. to review Waiting for reviews and removed 2. developing Work in progress labels Nov 27, 2020
@steffen-moser
Copy link

steffen-moser commented Nov 27, 2020

as the list there is also not flat according to my debug log.

is it possible that this was old information, perhaps pulled it from caching? Since it really only is assigned the value of $members. Also not that cachedGroupMembers is not a plain array, but an instance of CappedMemoryCache that behaves like one.

Of course, It is very well possible that this old information came from caching.

We encountered the problem on two servers, but only one of them is actually running using a cache. And only on this server, the if clause, where line 112 resides, was entered, and only there I needed the fix. I haven't done further testing, but maybe emptying the cache would have also helped (after fixing it at the right place where the cache gets filled).

@Hexasoft
Copy link

Hexasoft commented Nov 27, 2020

Sorry, I was too busy at work to test the last proposal.
Thank you for your job. For the last change, not sure if it can occurs, but is it certain that $dn = $user['dn'][0]; is always defined? I mean, does adding if (isset($user['dn'][0])) {} is of any interest?
@steffen-moser: before testing, I invalidated all caches (by changing TTL in configuration). Before doing this I also get strange behaviors.

@tofuSCHNITZEL
Copy link
Contributor

Thank you for your job. For the last change, not sure if it can occurs, but is it certain that $dn = $user['dn'][0]; is always defined? I mean, does adding if (isset($user['dn'][0])) {} is of any interest?

I think, since dn is such an integral attribute to ldap, it will always be a part of a ldap query response.

@blizzz
Copy link
Member Author

blizzz commented Nov 30, 2020

Thank you for your job. For the last change, not sure if it can occurs, but is it certain that $dn = $user['dn'][0]; is always defined? I mean, does adding if (isset($user['dn'][0])) {} is of any interest?

I think, since dn is such an integral attribute to ldap, it will always be a part of a ldap query response.

Yes, it always returns the dn as it is the de-facto (current!) identifier, even if you ask for a single different attribute:

dapsearch -LLL -a find -H ldap://localhost:7770 -D cn=root,dc=nc,dc=zara -W -b "ou=small,dc=nc,dc=zara" "uid=*" uid  
Enter LDAP Password: 
dn: uid=folk_0,ou=users,ou=small,dc=nc,dc=zara
uid: folk_0

dn: uid=folk_1,ou=users,ou=small,dc=nc,dc=zara
uid: folk_1

dn: uid=folk_2,ou=users,ou=small,dc=nc,dc=zara
uid: folk_2
…

@faily-bot
Copy link

faily-bot bot commented Nov 30, 2020

🤖 beep boop beep 🤖

Here are the logs for the failed build:

Status of 36131: failure

mariadb10.1-php7.3

Show full log
There were 2 warnings:

1) Test\Files\ViewTest::testRenameFailDeleteTargetKeepSource
Trying to configure method "writeStream" which cannot be configured because it does not exist, has not been specified, is final, or is static

2) Test\Files\ViewTest::testCopyFailDeleteTargetKeepSource
Trying to configure method "writeStream" which cannot be configured because it does not exist, has not been specified, is final, or is static

--

There was 1 failure:

1) Test\Files\ObjectStore\ObjectStoreStorageTest::testRenameOverWriteDirectory
Failed asserting that false matches expected 'foo'.

/drone/src/tests/lib/Files/ObjectStore/ObjectStoreStorageTest.php:148

@blizzz blizzz requested a review from rullzer December 1, 2020 12:20
Copy link
Member

@ChristophWurst ChristophWurst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 🐘

@rullzer rullzer mentioned this pull request Dec 14, 2020
59 tasks
Copy link
Contributor

@tofuSCHNITZEL tofuSCHNITZEL left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@blizzz blizzz merged commit f68cab4 into master Dec 15, 2020
@blizzz blizzz deleted the fix/24252/ldap-ingroup-memberid branch December 15, 2020 21:33
@blizzz
Copy link
Member Author

blizzz commented Dec 15, 2020

/backport to stable20

@blizzz
Copy link
Member Author

blizzz commented Dec 15, 2020

/backport to stable19

@backportbot-nextcloud
Copy link

The backport to stable19 failed. Please do this backport manually.

@tofuSCHNITZEL
Copy link
Contributor

@blizzz how will we do the stable19 backport? will we create a new branch and do the changes there manually? on what commit will this branch be based?

@blizzz
Copy link
Member Author

blizzz commented Dec 16, 2020

@blizzz how will we do the stable19 backport? will we create a new branch and do the changes there manually? on what commit will this branch be based?

Yes, we should do it. It's somewhere deep in my list, but if you want to beat me, that would be really appreciated :) A branch called backport/24402/stable19 shall be based against stable19. 'git cherry-pick' the commits in chronological order, and git mergetool to resolve conflicts.

@tofuSCHNITZEL
Copy link
Contributor

@blizzz how will we do the stable19 backport? will we create a new branch and do the changes there manually? on what commit will this branch be based?

Yes, we should do it. It's somewhere deep in my list, but if you want to beat me, that would be really appreciated :) A branch called backport/24402/stable19 shall be based against stable19. 'git cherry-pick' the commits in chronological order, and git mergetool to resolve conflicts.

okay tried my best ;) hopefully I did everything how its supposed to be done ;)
it was interesting that the auto merging at the first cherry-pick replaced the wrong $dns = array_merge($dns, $users); (the one inside the over 9000 if) I guess it just matched the first "similar" line occurrence since the line numbers were different

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

LDAP inGroup() failing
5 participants