|
| 1 | +<?php |
| 2 | + |
| 3 | +/** |
| 4 | + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors |
| 5 | + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. |
| 6 | + * SPDX-License-Identifier: AGPL-3.0-only |
| 7 | + */ |
| 8 | +namespace OCA\User_LDAP\Command; |
| 9 | + |
| 10 | +use OCA\User_LDAP\Group_Proxy; |
| 11 | +use OCA\User_LDAP\Helper; |
| 12 | +use OCA\User_LDAP\Mapping\GroupMapping; |
| 13 | +use OCA\User_LDAP\Mapping\UserMapping; |
| 14 | +use OCA\User_LDAP\User\DeletedUsersIndex; |
| 15 | +use OCA\User_LDAP\User_Proxy; |
| 16 | +use Symfony\Component\Console\Command\Command; |
| 17 | +use Symfony\Component\Console\Input\InputArgument; |
| 18 | +use Symfony\Component\Console\Input\InputInterface; |
| 19 | +use Symfony\Component\Console\Input\InputOption; |
| 20 | +use Symfony\Component\Console\Output\OutputInterface; |
| 21 | + |
| 22 | +class TestUserSettings extends Command { |
| 23 | + public function __construct( |
| 24 | + protected User_Proxy $backend, |
| 25 | + protected Group_Proxy $groupBackend, |
| 26 | + protected Helper $helper, |
| 27 | + protected DeletedUsersIndex $dui, |
| 28 | + protected UserMapping $mapping, |
| 29 | + protected GroupMapping $groupMapping, |
| 30 | + ) { |
| 31 | + parent::__construct(); |
| 32 | + } |
| 33 | + |
| 34 | + protected function configure(): void { |
| 35 | + $this |
| 36 | + ->setName('ldap:test-user-settings') |
| 37 | + ->setDescription('Runs tests and show information about user related LDAP settings') |
| 38 | + ->addArgument( |
| 39 | + 'user', |
| 40 | + InputArgument::REQUIRED, |
| 41 | + 'the user name as used in Nextcloud, or the LDAP DN' |
| 42 | + ) |
| 43 | + ->addOption( |
| 44 | + 'group', |
| 45 | + 'g', |
| 46 | + InputOption::VALUE_REQUIRED, |
| 47 | + 'A group DN to check if the user is a member or not' |
| 48 | + ) |
| 49 | + ->addOption( |
| 50 | + 'clearcache', |
| 51 | + null, |
| 52 | + InputOption::VALUE_NONE, |
| 53 | + 'Clear the cache of the LDAP connection before the beginning of tests' |
| 54 | + ) |
| 55 | + ; |
| 56 | + } |
| 57 | + |
| 58 | + protected function execute(InputInterface $input, OutputInterface $output): int { |
| 59 | + try { |
| 60 | + $uid = $input->getArgument('user'); |
| 61 | + $access = $this->backend->getLDAPAccess($uid); |
| 62 | + $connection = $access->getConnection(); |
| 63 | + if ($input->getOption('clearcache')) { |
| 64 | + $connection->clearCache(); |
| 65 | + } |
| 66 | + $configPrefix = $connection->getConfigPrefix(); |
| 67 | + $knownDn = ''; |
| 68 | + if ($access->stringResemblesDN($uid)) { |
| 69 | + $knownDn = $uid; |
| 70 | + $username = $access->dn2username($uid); |
| 71 | + if ($username !== false) { |
| 72 | + $uid = $username; |
| 73 | + } |
| 74 | + } |
| 75 | + |
| 76 | + $dn = $this->mapping->getDNByName($uid); |
| 77 | + if ($dn !== false) { |
| 78 | + $output->writeln("User <info>$dn</info> is mapped with account name <info>$uid</info>."); |
| 79 | + $uuid = $this->mapping->getUUIDByDN($dn); |
| 80 | + $output->writeln("Known UUID is <info>$uuid</info>."); |
| 81 | + if ($knownDn === '') { |
| 82 | + $knownDn = $dn; |
| 83 | + } |
| 84 | + } else { |
| 85 | + $output->writeln("User <info>$uid</info> is not mapped."); |
| 86 | + } |
| 87 | + |
| 88 | + if ($knownDn === '') { |
| 89 | + return self::SUCCESS; |
| 90 | + } |
| 91 | + |
| 92 | + if (!$access->isDNPartOfBase($knownDn, $access->getConnection()->ldapBaseUsers)) { |
| 93 | + $output->writeln( |
| 94 | + "User <info>$knownDn</info> is not in one of the configured user bases: <info>" . |
| 95 | + implode(',', $access->getConnection()->ldapBaseUsers) . |
| 96 | + '</info>.' |
| 97 | + ); |
| 98 | + } |
| 99 | + |
| 100 | + $output->writeln("Configuration prefix is <info>$configPrefix</info>"); |
| 101 | + $output->writeln(''); |
| 102 | + |
| 103 | + $attributeNames = [ |
| 104 | + 'ldapExpertUsernameAttr', |
| 105 | + 'ldapUuidUserAttribute', |
| 106 | + 'ldapExpertUUIDUserAttr', |
| 107 | + 'ldapQuotaAttribute', |
| 108 | + 'ldapEmailAttribute', |
| 109 | + 'ldapUserDisplayName', |
| 110 | + 'ldapUserDisplayName2', |
| 111 | + 'ldapExtStorageHomeAttribute', |
| 112 | + 'ldapAttributePhone', |
| 113 | + 'ldapAttributeWebsite', |
| 114 | + 'ldapAttributeAddress', |
| 115 | + 'ldapAttributeTwitter', |
| 116 | + 'ldapAttributeFediverse', |
| 117 | + 'ldapAttributeOrganisation', |
| 118 | + 'ldapAttributeRole', |
| 119 | + 'ldapAttributeHeadline', |
| 120 | + 'ldapAttributeBiography', |
| 121 | + 'ldapAttributeBirthDate', |
| 122 | + 'ldapAttributePronouns', |
| 123 | + ]; |
| 124 | + $output->writeln('Attributes set in configuration:'); |
| 125 | + foreach ($attributeNames as $attributeName) { |
| 126 | + if ($connection->$attributeName !== '') { |
| 127 | + $output->writeln("- $attributeName: <info>" . $connection->$attributeName . '</info>'); |
| 128 | + } |
| 129 | + } |
| 130 | + |
| 131 | + $filter = $connection->ldapUserFilter; |
| 132 | + $attrs = $access->userManager->getAttributes(true); |
| 133 | + $attrs[] = strtolower($connection->ldapExpertUsernameAttr); |
| 134 | + if ($connection->ldapUuidUserAttribute !== 'auto') { |
| 135 | + $attrs[] = strtolower($connection->ldapUuidUserAttribute); |
| 136 | + } |
| 137 | + $attrs[] = 'memberof'; |
| 138 | + $attrs = array_values(array_unique($attrs)); |
| 139 | + $attributes = $access->readAttributes($knownDn, $attrs, $filter); |
| 140 | + |
| 141 | + if ($attributes === false) { |
| 142 | + $output->writeln( |
| 143 | + "LDAP read on <info>$knownDn</info> with filter <info>$filter</info> failed." |
| 144 | + ); |
| 145 | + return self::FAILURE; |
| 146 | + } |
| 147 | + |
| 148 | + $output->writeln("Attributes fetched from LDAP using filter <info>$filter</info>:"); |
| 149 | + foreach ($attributes as $attribute => $value) { |
| 150 | + $output->writeln( |
| 151 | + "- $attribute: <info>" . json_encode($value) . '</info>' |
| 152 | + ); |
| 153 | + } |
| 154 | + |
| 155 | + $uuid = $access->getUUID($knownDn); |
| 156 | + if ($connection->ldapUuidUserAttribute === 'auto') { |
| 157 | + $output->writeln('<error>Failed to detect UUID attribute</error>'); |
| 158 | + } else { |
| 159 | + $output->writeln('Detected UUID attribute: <info>' . $connection->ldapUuidUserAttribute . '</info>'); |
| 160 | + } |
| 161 | + if ($uuid === false) { |
| 162 | + $output->writeln("<error>Failed to find UUID for $knownDn</error>"); |
| 163 | + } else { |
| 164 | + $output->writeln("UUID for <info>$knownDn</info>: <info>$uuid</info>"); |
| 165 | + } |
| 166 | + |
| 167 | + $groupLdapInstance = $this->groupBackend->getBackend($configPrefix); |
| 168 | + |
| 169 | + $output->writeln(''); |
| 170 | + $output->writeln('Group information:'); |
| 171 | + |
| 172 | + $attributeNames = [ |
| 173 | + 'ldapDynamicGroupMemberURL', |
| 174 | + 'ldapGroupFilter', |
| 175 | + 'ldapGroupMemberAssocAttr', |
| 176 | + ]; |
| 177 | + $output->writeln('Configuration:'); |
| 178 | + foreach ($attributeNames as $attributeName) { |
| 179 | + if ($connection->$attributeName !== '') { |
| 180 | + $output->writeln("- $attributeName: <info>" . $connection->$attributeName . '</info>'); |
| 181 | + } |
| 182 | + } |
| 183 | + |
| 184 | + $primaryGroup = $groupLdapInstance->getUserPrimaryGroup($knownDn); |
| 185 | + $output->writeln('Primary group: <info>' . ($primaryGroup !== false? $primaryGroup:'') . '</info>'); |
| 186 | + |
| 187 | + $groupByGid = $groupLdapInstance->getUserGroupByGid($knownDn); |
| 188 | + $output->writeln('Group from gidNumber: <info>' . ($groupByGid !== false? $groupByGid:'') . '</info>'); |
| 189 | + |
| 190 | + $groups = $groupLdapInstance->getUserGroups($uid); |
| 191 | + $output->writeln('All known groups: <info>' . json_encode($groups) . '</info>'); |
| 192 | + |
| 193 | + $memberOfUsed = ((int)$access->connection->hasMemberOfFilterSupport === 1 |
| 194 | + && (int)$access->connection->useMemberOfToDetectMembership === 1); |
| 195 | + |
| 196 | + $output->writeln('MemberOf usage: <info>' . ($memberOfUsed ? 'on' : 'off') . '</info> (' . $access->connection->hasMemberOfFilterSupport . ',' . $access->connection->useMemberOfToDetectMembership . ')'); |
| 197 | + |
| 198 | + $gid = (string)$input->getOption('group'); |
| 199 | + if ($gid === '') { |
| 200 | + return self::SUCCESS; |
| 201 | + } |
| 202 | + |
| 203 | + $output->writeln(''); |
| 204 | + $output->writeln("Group $gid:"); |
| 205 | + $knownGroupDn = ''; |
| 206 | + if ($access->stringResemblesDN($gid)) { |
| 207 | + $knownGroupDn = $gid; |
| 208 | + $groupname = $access->dn2groupname($gid); |
| 209 | + if ($groupname !== false) { |
| 210 | + $gid = $groupname; |
| 211 | + } |
| 212 | + } |
| 213 | + |
| 214 | + $groupDn = $this->groupMapping->getDNByName($gid); |
| 215 | + if ($groupDn !== false) { |
| 216 | + $output->writeln("Group <info>$groupDn</info> is mapped with name <info>$gid</info>."); |
| 217 | + $groupUuid = $this->groupMapping->getUUIDByDN($groupDn); |
| 218 | + $output->writeln("Known UUID is <info>$groupUuid</info>."); |
| 219 | + if ($knownGroupDn === '') { |
| 220 | + $knownGroupDn = $groupDn; |
| 221 | + } |
| 222 | + } else { |
| 223 | + $output->writeln("Group <info>$gid</info> is not mapped."); |
| 224 | + } |
| 225 | + |
| 226 | + $members = $groupLdapInstance->usersInGroup($gid); |
| 227 | + $output->writeln('Members: <info>' . json_encode($members) . '</info>'); |
| 228 | + |
| 229 | + return self::SUCCESS; |
| 230 | + |
| 231 | + } catch (\Exception $e) { |
| 232 | + $output->writeln('<error>' . $e->getMessage() . '</error>'); |
| 233 | + return self::FAILURE; |
| 234 | + } |
| 235 | + } |
| 236 | +} |
0 commit comments