@@ -14,290 +14,290 @@ |
||
| 14 | 14 | use OCP\Server; |
| 15 | 15 | |
| 16 | 16 | class Helper { |
| 17 | - /** @var CappedMemoryCache<string> */ |
|
| 18 | - protected CappedMemoryCache $sanitizeDnCache; |
|
| 19 | - |
|
| 20 | - public function __construct( |
|
| 21 | - private IAppConfig $appConfig, |
|
| 22 | - private IDBConnection $connection, |
|
| 23 | - ) { |
|
| 24 | - $this->sanitizeDnCache = new CappedMemoryCache(10000); |
|
| 25 | - } |
|
| 26 | - |
|
| 27 | - /** |
|
| 28 | - * returns prefixes for each saved LDAP/AD server configuration. |
|
| 29 | - * |
|
| 30 | - * @param bool $activeConfigurations optional, whether only active configuration shall be |
|
| 31 | - * retrieved, defaults to false |
|
| 32 | - * @return array with a list of the available prefixes |
|
| 33 | - * |
|
| 34 | - * Configuration prefixes are used to set up configurations for n LDAP or |
|
| 35 | - * AD servers. Since configuration is stored in the database, table |
|
| 36 | - * appconfig under appid user_ldap, the common identifiers in column |
|
| 37 | - * 'configkey' have a prefix. The prefix for the very first server |
|
| 38 | - * configuration is empty. |
|
| 39 | - * Configkey Examples: |
|
| 40 | - * Server 1: ldap_login_filter |
|
| 41 | - * Server 2: s1_ldap_login_filter |
|
| 42 | - * Server 3: s2_ldap_login_filter |
|
| 43 | - * |
|
| 44 | - * The prefix needs to be passed to the constructor of Connection class, |
|
| 45 | - * except the default (first) server shall be connected to. |
|
| 46 | - * |
|
| 47 | - */ |
|
| 48 | - public function getServerConfigurationPrefixes(bool $activeConfigurations = false): array { |
|
| 49 | - $all = $this->getAllServerConfigurationPrefixes(); |
|
| 50 | - if (!$activeConfigurations) { |
|
| 51 | - return $all; |
|
| 52 | - } |
|
| 53 | - return array_values(array_filter( |
|
| 54 | - $all, |
|
| 55 | - fn (string $prefix): bool => ($this->appConfig->getValueString('user_ldap', $prefix . 'ldap_configuration_active') === '1') |
|
| 56 | - )); |
|
| 57 | - } |
|
| 58 | - |
|
| 59 | - protected function getAllServerConfigurationPrefixes(): array { |
|
| 60 | - $unfilled = ['UNFILLED']; |
|
| 61 | - $prefixes = $this->appConfig->getValueArray('user_ldap', 'configuration_prefixes', $unfilled); |
|
| 62 | - if ($prefixes !== $unfilled) { |
|
| 63 | - return $prefixes; |
|
| 64 | - } |
|
| 65 | - |
|
| 66 | - /* Fallback to browsing key for migration from Nextcloud<32 */ |
|
| 67 | - $referenceConfigkey = 'ldap_configuration_active'; |
|
| 68 | - |
|
| 69 | - $keys = $this->getServersConfig($referenceConfigkey); |
|
| 70 | - |
|
| 71 | - $prefixes = []; |
|
| 72 | - foreach ($keys as $key) { |
|
| 73 | - $len = strlen($key) - strlen($referenceConfigkey); |
|
| 74 | - $prefixes[] = substr($key, 0, $len); |
|
| 75 | - } |
|
| 76 | - sort($prefixes); |
|
| 77 | - |
|
| 78 | - $this->appConfig->setValueArray('user_ldap', 'configuration_prefixes', $prefixes); |
|
| 79 | - |
|
| 80 | - return $prefixes; |
|
| 81 | - } |
|
| 82 | - |
|
| 83 | - /** |
|
| 84 | - * |
|
| 85 | - * determines the host for every configured connection |
|
| 86 | - * |
|
| 87 | - * @return array<string,string> an array with configprefix as keys |
|
| 88 | - * |
|
| 89 | - */ |
|
| 90 | - public function getServerConfigurationHosts(): array { |
|
| 91 | - $prefixes = $this->getServerConfigurationPrefixes(); |
|
| 92 | - |
|
| 93 | - $referenceConfigkey = 'ldap_host'; |
|
| 94 | - $result = []; |
|
| 95 | - foreach ($prefixes as $prefix) { |
|
| 96 | - $result[$prefix] = $this->appConfig->getValueString('user_ldap', $prefix . $referenceConfigkey); |
|
| 97 | - } |
|
| 98 | - |
|
| 99 | - return $result; |
|
| 100 | - } |
|
| 101 | - |
|
| 102 | - /** |
|
| 103 | - * return the next available configuration prefix and register it as used |
|
| 104 | - */ |
|
| 105 | - public function getNextServerConfigurationPrefix(): string { |
|
| 106 | - $prefixes = $this->getServerConfigurationPrefixes(); |
|
| 107 | - |
|
| 108 | - if (count($prefixes) === 0) { |
|
| 109 | - $prefix = 's01'; |
|
| 110 | - } else { |
|
| 111 | - sort($prefixes); |
|
| 112 | - $lastKey = end($prefixes); |
|
| 113 | - $lastNumber = (int)str_replace('s', '', $lastKey); |
|
| 114 | - $prefix = 's' . str_pad((string)($lastNumber + 1), 2, '0', STR_PAD_LEFT); |
|
| 115 | - } |
|
| 116 | - |
|
| 117 | - $prefixes[] = $prefix; |
|
| 118 | - $this->appConfig->setValueArray('user_ldap', 'configuration_prefixes', $prefixes); |
|
| 119 | - return $prefix; |
|
| 120 | - } |
|
| 121 | - |
|
| 122 | - private function getServersConfig(string $value): array { |
|
| 123 | - $regex = '/' . $value . '$/S'; |
|
| 124 | - |
|
| 125 | - $keys = $this->appConfig->getKeys('user_ldap'); |
|
| 126 | - $result = []; |
|
| 127 | - foreach ($keys as $key) { |
|
| 128 | - if (preg_match($regex, $key) === 1) { |
|
| 129 | - $result[] = $key; |
|
| 130 | - } |
|
| 131 | - } |
|
| 132 | - |
|
| 133 | - return $result; |
|
| 134 | - } |
|
| 135 | - |
|
| 136 | - /** |
|
| 137 | - * deletes a given saved LDAP/AD server configuration. |
|
| 138 | - * |
|
| 139 | - * @param string $prefix the configuration prefix of the config to delete |
|
| 140 | - * @return bool true on success, false otherwise |
|
| 141 | - */ |
|
| 142 | - public function deleteServerConfiguration($prefix) { |
|
| 143 | - $prefixes = $this->getServerConfigurationPrefixes(); |
|
| 144 | - $index = array_search($prefix, $prefixes); |
|
| 145 | - if ($index === false) { |
|
| 146 | - return false; |
|
| 147 | - } |
|
| 148 | - |
|
| 149 | - $query = $this->connection->getQueryBuilder(); |
|
| 150 | - $query->delete('appconfig') |
|
| 151 | - ->where($query->expr()->eq('appid', $query->createNamedParameter('user_ldap'))) |
|
| 152 | - ->andWhere($query->expr()->like('configkey', $query->createNamedParameter((string)$prefix . '%'))) |
|
| 153 | - ->andWhere($query->expr()->notIn('configkey', $query->createNamedParameter([ |
|
| 154 | - 'enabled', |
|
| 155 | - 'installed_version', |
|
| 156 | - 'types', |
|
| 157 | - 'bgjUpdateGroupsLastRun', |
|
| 158 | - ], IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 159 | - |
|
| 160 | - if (empty($prefix)) { |
|
| 161 | - $query->andWhere($query->expr()->notLike('configkey', $query->createNamedParameter('s%'))); |
|
| 162 | - } |
|
| 163 | - |
|
| 164 | - $deletedRows = $query->executeStatement(); |
|
| 165 | - |
|
| 166 | - unset($prefixes[$index]); |
|
| 167 | - $this->appConfig->setValueArray('user_ldap', 'configuration_prefixes', array_values($prefixes)); |
|
| 168 | - |
|
| 169 | - return $deletedRows !== 0; |
|
| 170 | - } |
|
| 171 | - |
|
| 172 | - /** |
|
| 173 | - * checks whether there is one or more disabled LDAP configurations |
|
| 174 | - */ |
|
| 175 | - public function haveDisabledConfigurations(): bool { |
|
| 176 | - $all = $this->getServerConfigurationPrefixes(); |
|
| 177 | - foreach ($all as $prefix) { |
|
| 178 | - if ($this->appConfig->getValueString('user_ldap', $prefix . 'ldap_configuration_active') !== '1') { |
|
| 179 | - return true; |
|
| 180 | - } |
|
| 181 | - } |
|
| 182 | - return false; |
|
| 183 | - } |
|
| 184 | - |
|
| 185 | - /** |
|
| 186 | - * extracts the domain from a given URL |
|
| 187 | - * |
|
| 188 | - * @param string $url the URL |
|
| 189 | - * @return string|false domain as string on success, false otherwise |
|
| 190 | - */ |
|
| 191 | - public function getDomainFromURL($url) { |
|
| 192 | - $uinfo = parse_url($url); |
|
| 193 | - if (!is_array($uinfo)) { |
|
| 194 | - return false; |
|
| 195 | - } |
|
| 196 | - |
|
| 197 | - $domain = false; |
|
| 198 | - if (isset($uinfo['host'])) { |
|
| 199 | - $domain = $uinfo['host']; |
|
| 200 | - } elseif (isset($uinfo['path'])) { |
|
| 201 | - $domain = $uinfo['path']; |
|
| 202 | - } |
|
| 203 | - |
|
| 204 | - return $domain; |
|
| 205 | - } |
|
| 206 | - |
|
| 207 | - /** |
|
| 208 | - * sanitizes a DN received from the LDAP server |
|
| 209 | - * |
|
| 210 | - * This is used and done to have a stable format of DNs that can be compared |
|
| 211 | - * and identified again. The input DN value is modified as following: |
|
| 212 | - * |
|
| 213 | - * 1) whitespaces after commas are removed |
|
| 214 | - * 2) the DN is turned to lower-case |
|
| 215 | - * 3) the DN is escaped according to RFC 2253 |
|
| 216 | - * |
|
| 217 | - * When a future DN is supposed to be used as a base parameter, it has to be |
|
| 218 | - * run through DNasBaseParameter() first, to recode \5c into a backslash |
|
| 219 | - * again, otherwise the search or read operation will fail with LDAP error |
|
| 220 | - * 32, NO_SUCH_OBJECT. Regular usage in LDAP filters requires the backslash |
|
| 221 | - * being escaped, however. |
|
| 222 | - * |
|
| 223 | - * Internally, DNs are stored in their sanitized form. |
|
| 224 | - * |
|
| 225 | - * @param array|string $dn the DN in question |
|
| 226 | - * @return array|string the sanitized DN |
|
| 227 | - */ |
|
| 228 | - public function sanitizeDN($dn) { |
|
| 229 | - //treating multiple base DNs |
|
| 230 | - if (is_array($dn)) { |
|
| 231 | - $result = []; |
|
| 232 | - foreach ($dn as $singleDN) { |
|
| 233 | - $result[] = $this->sanitizeDN($singleDN); |
|
| 234 | - } |
|
| 235 | - return $result; |
|
| 236 | - } |
|
| 237 | - |
|
| 238 | - if (!is_string($dn)) { |
|
| 239 | - throw new \LogicException('String expected ' . \gettype($dn) . ' given'); |
|
| 240 | - } |
|
| 241 | - |
|
| 242 | - if (($sanitizedDn = $this->sanitizeDnCache->get($dn)) !== null) { |
|
| 243 | - return $sanitizedDn; |
|
| 244 | - } |
|
| 245 | - |
|
| 246 | - //OID sometimes gives back DNs with whitespace after the comma |
|
| 247 | - // a la "uid=foo, cn=bar, dn=..." We need to tackle this! |
|
| 248 | - $sanitizedDn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); |
|
| 249 | - |
|
| 250 | - //make comparisons and everything work |
|
| 251 | - $sanitizedDn = mb_strtolower($sanitizedDn, 'UTF-8'); |
|
| 252 | - |
|
| 253 | - //escape DN values according to RFC 2253 – this is already done by ldap_explode_dn |
|
| 254 | - //to use the DN in search filters, \ needs to be escaped to \5c additionally |
|
| 255 | - //to use them in bases, we convert them back to simple backslashes in readAttribute() |
|
| 256 | - $replacements = [ |
|
| 257 | - '\,' => '\5c2C', |
|
| 258 | - '\=' => '\5c3D', |
|
| 259 | - '\+' => '\5c2B', |
|
| 260 | - '\<' => '\5c3C', |
|
| 261 | - '\>' => '\5c3E', |
|
| 262 | - '\;' => '\5c3B', |
|
| 263 | - '\"' => '\5c22', |
|
| 264 | - '\#' => '\5c23', |
|
| 265 | - '(' => '\28', |
|
| 266 | - ')' => '\29', |
|
| 267 | - '*' => '\2A', |
|
| 268 | - ]; |
|
| 269 | - $sanitizedDn = str_replace(array_keys($replacements), array_values($replacements), $sanitizedDn); |
|
| 270 | - $this->sanitizeDnCache->set($dn, $sanitizedDn); |
|
| 271 | - |
|
| 272 | - return $sanitizedDn; |
|
| 273 | - } |
|
| 274 | - |
|
| 275 | - /** |
|
| 276 | - * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters |
|
| 277 | - * |
|
| 278 | - * @param string $dn the DN |
|
| 279 | - * @return string |
|
| 280 | - */ |
|
| 281 | - public function DNasBaseParameter($dn) { |
|
| 282 | - return str_ireplace('\\5c', '\\', $dn); |
|
| 283 | - } |
|
| 284 | - |
|
| 285 | - /** |
|
| 286 | - * listens to a hook thrown by server2server sharing and replaces the given |
|
| 287 | - * login name by a username, if it matches an LDAP user. |
|
| 288 | - * |
|
| 289 | - * @param array $param contains a reference to a $uid var under 'uid' key |
|
| 290 | - * @throws \Exception |
|
| 291 | - */ |
|
| 292 | - public static function loginName2UserName($param): void { |
|
| 293 | - if (!isset($param['uid'])) { |
|
| 294 | - throw new \Exception('key uid is expected to be set in $param'); |
|
| 295 | - } |
|
| 296 | - |
|
| 297 | - $userBackend = Server::get(User_Proxy::class); |
|
| 298 | - $uid = $userBackend->loginName2UserName($param['uid']); |
|
| 299 | - if ($uid !== false) { |
|
| 300 | - $param['uid'] = $uid; |
|
| 301 | - } |
|
| 302 | - } |
|
| 17 | + /** @var CappedMemoryCache<string> */ |
|
| 18 | + protected CappedMemoryCache $sanitizeDnCache; |
|
| 19 | + |
|
| 20 | + public function __construct( |
|
| 21 | + private IAppConfig $appConfig, |
|
| 22 | + private IDBConnection $connection, |
|
| 23 | + ) { |
|
| 24 | + $this->sanitizeDnCache = new CappedMemoryCache(10000); |
|
| 25 | + } |
|
| 26 | + |
|
| 27 | + /** |
|
| 28 | + * returns prefixes for each saved LDAP/AD server configuration. |
|
| 29 | + * |
|
| 30 | + * @param bool $activeConfigurations optional, whether only active configuration shall be |
|
| 31 | + * retrieved, defaults to false |
|
| 32 | + * @return array with a list of the available prefixes |
|
| 33 | + * |
|
| 34 | + * Configuration prefixes are used to set up configurations for n LDAP or |
|
| 35 | + * AD servers. Since configuration is stored in the database, table |
|
| 36 | + * appconfig under appid user_ldap, the common identifiers in column |
|
| 37 | + * 'configkey' have a prefix. The prefix for the very first server |
|
| 38 | + * configuration is empty. |
|
| 39 | + * Configkey Examples: |
|
| 40 | + * Server 1: ldap_login_filter |
|
| 41 | + * Server 2: s1_ldap_login_filter |
|
| 42 | + * Server 3: s2_ldap_login_filter |
|
| 43 | + * |
|
| 44 | + * The prefix needs to be passed to the constructor of Connection class, |
|
| 45 | + * except the default (first) server shall be connected to. |
|
| 46 | + * |
|
| 47 | + */ |
|
| 48 | + public function getServerConfigurationPrefixes(bool $activeConfigurations = false): array { |
|
| 49 | + $all = $this->getAllServerConfigurationPrefixes(); |
|
| 50 | + if (!$activeConfigurations) { |
|
| 51 | + return $all; |
|
| 52 | + } |
|
| 53 | + return array_values(array_filter( |
|
| 54 | + $all, |
|
| 55 | + fn (string $prefix): bool => ($this->appConfig->getValueString('user_ldap', $prefix . 'ldap_configuration_active') === '1') |
|
| 56 | + )); |
|
| 57 | + } |
|
| 58 | + |
|
| 59 | + protected function getAllServerConfigurationPrefixes(): array { |
|
| 60 | + $unfilled = ['UNFILLED']; |
|
| 61 | + $prefixes = $this->appConfig->getValueArray('user_ldap', 'configuration_prefixes', $unfilled); |
|
| 62 | + if ($prefixes !== $unfilled) { |
|
| 63 | + return $prefixes; |
|
| 64 | + } |
|
| 65 | + |
|
| 66 | + /* Fallback to browsing key for migration from Nextcloud<32 */ |
|
| 67 | + $referenceConfigkey = 'ldap_configuration_active'; |
|
| 68 | + |
|
| 69 | + $keys = $this->getServersConfig($referenceConfigkey); |
|
| 70 | + |
|
| 71 | + $prefixes = []; |
|
| 72 | + foreach ($keys as $key) { |
|
| 73 | + $len = strlen($key) - strlen($referenceConfigkey); |
|
| 74 | + $prefixes[] = substr($key, 0, $len); |
|
| 75 | + } |
|
| 76 | + sort($prefixes); |
|
| 77 | + |
|
| 78 | + $this->appConfig->setValueArray('user_ldap', 'configuration_prefixes', $prefixes); |
|
| 79 | + |
|
| 80 | + return $prefixes; |
|
| 81 | + } |
|
| 82 | + |
|
| 83 | + /** |
|
| 84 | + * |
|
| 85 | + * determines the host for every configured connection |
|
| 86 | + * |
|
| 87 | + * @return array<string,string> an array with configprefix as keys |
|
| 88 | + * |
|
| 89 | + */ |
|
| 90 | + public function getServerConfigurationHosts(): array { |
|
| 91 | + $prefixes = $this->getServerConfigurationPrefixes(); |
|
| 92 | + |
|
| 93 | + $referenceConfigkey = 'ldap_host'; |
|
| 94 | + $result = []; |
|
| 95 | + foreach ($prefixes as $prefix) { |
|
| 96 | + $result[$prefix] = $this->appConfig->getValueString('user_ldap', $prefix . $referenceConfigkey); |
|
| 97 | + } |
|
| 98 | + |
|
| 99 | + return $result; |
|
| 100 | + } |
|
| 101 | + |
|
| 102 | + /** |
|
| 103 | + * return the next available configuration prefix and register it as used |
|
| 104 | + */ |
|
| 105 | + public function getNextServerConfigurationPrefix(): string { |
|
| 106 | + $prefixes = $this->getServerConfigurationPrefixes(); |
|
| 107 | + |
|
| 108 | + if (count($prefixes) === 0) { |
|
| 109 | + $prefix = 's01'; |
|
| 110 | + } else { |
|
| 111 | + sort($prefixes); |
|
| 112 | + $lastKey = end($prefixes); |
|
| 113 | + $lastNumber = (int)str_replace('s', '', $lastKey); |
|
| 114 | + $prefix = 's' . str_pad((string)($lastNumber + 1), 2, '0', STR_PAD_LEFT); |
|
| 115 | + } |
|
| 116 | + |
|
| 117 | + $prefixes[] = $prefix; |
|
| 118 | + $this->appConfig->setValueArray('user_ldap', 'configuration_prefixes', $prefixes); |
|
| 119 | + return $prefix; |
|
| 120 | + } |
|
| 121 | + |
|
| 122 | + private function getServersConfig(string $value): array { |
|
| 123 | + $regex = '/' . $value . '$/S'; |
|
| 124 | + |
|
| 125 | + $keys = $this->appConfig->getKeys('user_ldap'); |
|
| 126 | + $result = []; |
|
| 127 | + foreach ($keys as $key) { |
|
| 128 | + if (preg_match($regex, $key) === 1) { |
|
| 129 | + $result[] = $key; |
|
| 130 | + } |
|
| 131 | + } |
|
| 132 | + |
|
| 133 | + return $result; |
|
| 134 | + } |
|
| 135 | + |
|
| 136 | + /** |
|
| 137 | + * deletes a given saved LDAP/AD server configuration. |
|
| 138 | + * |
|
| 139 | + * @param string $prefix the configuration prefix of the config to delete |
|
| 140 | + * @return bool true on success, false otherwise |
|
| 141 | + */ |
|
| 142 | + public function deleteServerConfiguration($prefix) { |
|
| 143 | + $prefixes = $this->getServerConfigurationPrefixes(); |
|
| 144 | + $index = array_search($prefix, $prefixes); |
|
| 145 | + if ($index === false) { |
|
| 146 | + return false; |
|
| 147 | + } |
|
| 148 | + |
|
| 149 | + $query = $this->connection->getQueryBuilder(); |
|
| 150 | + $query->delete('appconfig') |
|
| 151 | + ->where($query->expr()->eq('appid', $query->createNamedParameter('user_ldap'))) |
|
| 152 | + ->andWhere($query->expr()->like('configkey', $query->createNamedParameter((string)$prefix . '%'))) |
|
| 153 | + ->andWhere($query->expr()->notIn('configkey', $query->createNamedParameter([ |
|
| 154 | + 'enabled', |
|
| 155 | + 'installed_version', |
|
| 156 | + 'types', |
|
| 157 | + 'bgjUpdateGroupsLastRun', |
|
| 158 | + ], IQueryBuilder::PARAM_STR_ARRAY))); |
|
| 159 | + |
|
| 160 | + if (empty($prefix)) { |
|
| 161 | + $query->andWhere($query->expr()->notLike('configkey', $query->createNamedParameter('s%'))); |
|
| 162 | + } |
|
| 163 | + |
|
| 164 | + $deletedRows = $query->executeStatement(); |
|
| 165 | + |
|
| 166 | + unset($prefixes[$index]); |
|
| 167 | + $this->appConfig->setValueArray('user_ldap', 'configuration_prefixes', array_values($prefixes)); |
|
| 168 | + |
|
| 169 | + return $deletedRows !== 0; |
|
| 170 | + } |
|
| 171 | + |
|
| 172 | + /** |
|
| 173 | + * checks whether there is one or more disabled LDAP configurations |
|
| 174 | + */ |
|
| 175 | + public function haveDisabledConfigurations(): bool { |
|
| 176 | + $all = $this->getServerConfigurationPrefixes(); |
|
| 177 | + foreach ($all as $prefix) { |
|
| 178 | + if ($this->appConfig->getValueString('user_ldap', $prefix . 'ldap_configuration_active') !== '1') { |
|
| 179 | + return true; |
|
| 180 | + } |
|
| 181 | + } |
|
| 182 | + return false; |
|
| 183 | + } |
|
| 184 | + |
|
| 185 | + /** |
|
| 186 | + * extracts the domain from a given URL |
|
| 187 | + * |
|
| 188 | + * @param string $url the URL |
|
| 189 | + * @return string|false domain as string on success, false otherwise |
|
| 190 | + */ |
|
| 191 | + public function getDomainFromURL($url) { |
|
| 192 | + $uinfo = parse_url($url); |
|
| 193 | + if (!is_array($uinfo)) { |
|
| 194 | + return false; |
|
| 195 | + } |
|
| 196 | + |
|
| 197 | + $domain = false; |
|
| 198 | + if (isset($uinfo['host'])) { |
|
| 199 | + $domain = $uinfo['host']; |
|
| 200 | + } elseif (isset($uinfo['path'])) { |
|
| 201 | + $domain = $uinfo['path']; |
|
| 202 | + } |
|
| 203 | + |
|
| 204 | + return $domain; |
|
| 205 | + } |
|
| 206 | + |
|
| 207 | + /** |
|
| 208 | + * sanitizes a DN received from the LDAP server |
|
| 209 | + * |
|
| 210 | + * This is used and done to have a stable format of DNs that can be compared |
|
| 211 | + * and identified again. The input DN value is modified as following: |
|
| 212 | + * |
|
| 213 | + * 1) whitespaces after commas are removed |
|
| 214 | + * 2) the DN is turned to lower-case |
|
| 215 | + * 3) the DN is escaped according to RFC 2253 |
|
| 216 | + * |
|
| 217 | + * When a future DN is supposed to be used as a base parameter, it has to be |
|
| 218 | + * run through DNasBaseParameter() first, to recode \5c into a backslash |
|
| 219 | + * again, otherwise the search or read operation will fail with LDAP error |
|
| 220 | + * 32, NO_SUCH_OBJECT. Regular usage in LDAP filters requires the backslash |
|
| 221 | + * being escaped, however. |
|
| 222 | + * |
|
| 223 | + * Internally, DNs are stored in their sanitized form. |
|
| 224 | + * |
|
| 225 | + * @param array|string $dn the DN in question |
|
| 226 | + * @return array|string the sanitized DN |
|
| 227 | + */ |
|
| 228 | + public function sanitizeDN($dn) { |
|
| 229 | + //treating multiple base DNs |
|
| 230 | + if (is_array($dn)) { |
|
| 231 | + $result = []; |
|
| 232 | + foreach ($dn as $singleDN) { |
|
| 233 | + $result[] = $this->sanitizeDN($singleDN); |
|
| 234 | + } |
|
| 235 | + return $result; |
|
| 236 | + } |
|
| 237 | + |
|
| 238 | + if (!is_string($dn)) { |
|
| 239 | + throw new \LogicException('String expected ' . \gettype($dn) . ' given'); |
|
| 240 | + } |
|
| 241 | + |
|
| 242 | + if (($sanitizedDn = $this->sanitizeDnCache->get($dn)) !== null) { |
|
| 243 | + return $sanitizedDn; |
|
| 244 | + } |
|
| 245 | + |
|
| 246 | + //OID sometimes gives back DNs with whitespace after the comma |
|
| 247 | + // a la "uid=foo, cn=bar, dn=..." We need to tackle this! |
|
| 248 | + $sanitizedDn = preg_replace('/([^\\\]),(\s+)/u', '\1,', $dn); |
|
| 249 | + |
|
| 250 | + //make comparisons and everything work |
|
| 251 | + $sanitizedDn = mb_strtolower($sanitizedDn, 'UTF-8'); |
|
| 252 | + |
|
| 253 | + //escape DN values according to RFC 2253 – this is already done by ldap_explode_dn |
|
| 254 | + //to use the DN in search filters, \ needs to be escaped to \5c additionally |
|
| 255 | + //to use them in bases, we convert them back to simple backslashes in readAttribute() |
|
| 256 | + $replacements = [ |
|
| 257 | + '\,' => '\5c2C', |
|
| 258 | + '\=' => '\5c3D', |
|
| 259 | + '\+' => '\5c2B', |
|
| 260 | + '\<' => '\5c3C', |
|
| 261 | + '\>' => '\5c3E', |
|
| 262 | + '\;' => '\5c3B', |
|
| 263 | + '\"' => '\5c22', |
|
| 264 | + '\#' => '\5c23', |
|
| 265 | + '(' => '\28', |
|
| 266 | + ')' => '\29', |
|
| 267 | + '*' => '\2A', |
|
| 268 | + ]; |
|
| 269 | + $sanitizedDn = str_replace(array_keys($replacements), array_values($replacements), $sanitizedDn); |
|
| 270 | + $this->sanitizeDnCache->set($dn, $sanitizedDn); |
|
| 271 | + |
|
| 272 | + return $sanitizedDn; |
|
| 273 | + } |
|
| 274 | + |
|
| 275 | + /** |
|
| 276 | + * converts a stored DN so it can be used as base parameter for LDAP queries, internally we store them for usage in LDAP filters |
|
| 277 | + * |
|
| 278 | + * @param string $dn the DN |
|
| 279 | + * @return string |
|
| 280 | + */ |
|
| 281 | + public function DNasBaseParameter($dn) { |
|
| 282 | + return str_ireplace('\\5c', '\\', $dn); |
|
| 283 | + } |
|
| 284 | + |
|
| 285 | + /** |
|
| 286 | + * listens to a hook thrown by server2server sharing and replaces the given |
|
| 287 | + * login name by a username, if it matches an LDAP user. |
|
| 288 | + * |
|
| 289 | + * @param array $param contains a reference to a $uid var under 'uid' key |
|
| 290 | + * @throws \Exception |
|
| 291 | + */ |
|
| 292 | + public static function loginName2UserName($param): void { |
|
| 293 | + if (!isset($param['uid'])) { |
|
| 294 | + throw new \Exception('key uid is expected to be set in $param'); |
|
| 295 | + } |
|
| 296 | + |
|
| 297 | + $userBackend = Server::get(User_Proxy::class); |
|
| 298 | + $uid = $userBackend->loginName2UserName($param['uid']); |
|
| 299 | + if ($uid !== false) { |
|
| 300 | + $param['uid'] = $uid; |
|
| 301 | + } |
|
| 302 | + } |
|
| 303 | 303 | } |
@@ -52,7 +52,7 @@ discard block |
||
| 52 | 52 | } |
| 53 | 53 | return array_values(array_filter( |
| 54 | 54 | $all, |
| 55 | - fn (string $prefix): bool => ($this->appConfig->getValueString('user_ldap', $prefix . 'ldap_configuration_active') === '1') |
|
| 55 | + fn (string $prefix): bool => ($this->appConfig->getValueString('user_ldap', $prefix.'ldap_configuration_active') === '1') |
|
| 56 | 56 | )); |
| 57 | 57 | } |
| 58 | 58 | |
@@ -93,7 +93,7 @@ discard block |
||
| 93 | 93 | $referenceConfigkey = 'ldap_host'; |
| 94 | 94 | $result = []; |
| 95 | 95 | foreach ($prefixes as $prefix) { |
| 96 | - $result[$prefix] = $this->appConfig->getValueString('user_ldap', $prefix . $referenceConfigkey); |
|
| 96 | + $result[$prefix] = $this->appConfig->getValueString('user_ldap', $prefix.$referenceConfigkey); |
|
| 97 | 97 | } |
| 98 | 98 | |
| 99 | 99 | return $result; |
@@ -110,8 +110,8 @@ discard block |
||
| 110 | 110 | } else { |
| 111 | 111 | sort($prefixes); |
| 112 | 112 | $lastKey = end($prefixes); |
| 113 | - $lastNumber = (int)str_replace('s', '', $lastKey); |
|
| 114 | - $prefix = 's' . str_pad((string)($lastNumber + 1), 2, '0', STR_PAD_LEFT); |
|
| 113 | + $lastNumber = (int) str_replace('s', '', $lastKey); |
|
| 114 | + $prefix = 's'.str_pad((string) ($lastNumber + 1), 2, '0', STR_PAD_LEFT); |
|
| 115 | 115 | } |
| 116 | 116 | |
| 117 | 117 | $prefixes[] = $prefix; |
@@ -120,7 +120,7 @@ discard block |
||
| 120 | 120 | } |
| 121 | 121 | |
| 122 | 122 | private function getServersConfig(string $value): array { |
| 123 | - $regex = '/' . $value . '$/S'; |
|
| 123 | + $regex = '/'.$value.'$/S'; |
|
| 124 | 124 | |
| 125 | 125 | $keys = $this->appConfig->getKeys('user_ldap'); |
| 126 | 126 | $result = []; |
@@ -149,7 +149,7 @@ discard block |
||
| 149 | 149 | $query = $this->connection->getQueryBuilder(); |
| 150 | 150 | $query->delete('appconfig') |
| 151 | 151 | ->where($query->expr()->eq('appid', $query->createNamedParameter('user_ldap'))) |
| 152 | - ->andWhere($query->expr()->like('configkey', $query->createNamedParameter((string)$prefix . '%'))) |
|
| 152 | + ->andWhere($query->expr()->like('configkey', $query->createNamedParameter((string) $prefix.'%'))) |
|
| 153 | 153 | ->andWhere($query->expr()->notIn('configkey', $query->createNamedParameter([ |
| 154 | 154 | 'enabled', |
| 155 | 155 | 'installed_version', |
@@ -175,7 +175,7 @@ discard block |
||
| 175 | 175 | public function haveDisabledConfigurations(): bool { |
| 176 | 176 | $all = $this->getServerConfigurationPrefixes(); |
| 177 | 177 | foreach ($all as $prefix) { |
| 178 | - if ($this->appConfig->getValueString('user_ldap', $prefix . 'ldap_configuration_active') !== '1') { |
|
| 178 | + if ($this->appConfig->getValueString('user_ldap', $prefix.'ldap_configuration_active') !== '1') { |
|
| 179 | 179 | return true; |
| 180 | 180 | } |
| 181 | 181 | } |
@@ -236,7 +236,7 @@ discard block |
||
| 236 | 236 | } |
| 237 | 237 | |
| 238 | 238 | if (!is_string($dn)) { |
| 239 | - throw new \LogicException('String expected ' . \gettype($dn) . ' given'); |
|
| 239 | + throw new \LogicException('String expected '.\gettype($dn).' given'); |
|
| 240 | 240 | } |
| 241 | 241 | |
| 242 | 242 | if (($sanitizedDn = $this->sanitizeDnCache->get($dn)) !== null) { |