| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | <?php | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace BookStack\Auth\Access; | 
					
						
							| 
									
										
										
										
											2016-01-10 03:23:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-25 19:30:50 +08:00
										 |  |  | use BookStack\Auth\User; | 
					
						
							| 
									
										
										
										
											2020-02-16 04:31:23 +08:00
										 |  |  | use BookStack\Exceptions\JsonDebugException; | 
					
						
							| 
									
										
										
										
											2018-09-25 23:58:03 +08:00
										 |  |  | use BookStack\Exceptions\LdapException; | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  | use BookStack\Uploads\UserAvatars; | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  | use ErrorException; | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  | use Illuminate\Support\Facades\Log; | 
					
						
							| 
									
										
										
										
											2016-01-10 03:23:35 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-01-16 07:21:47 +08:00
										 |  |  | /** | 
					
						
							|  |  |  |  * Class LdapService | 
					
						
							|  |  |  |  * Handles any app-specific LDAP tasks. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2021-10-07 06:05:26 +08:00
										 |  |  | class LdapService | 
					
						
							| 
									
										
										
										
											2016-01-10 03:23:35 +08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2022-03-24 00:34:23 +08:00
										 |  |  |     protected Ldap $ldap; | 
					
						
							|  |  |  |     protected GroupSyncService $groupSyncService; | 
					
						
							|  |  |  |     protected UserAvatars $userAvatars; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * @var resource | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     protected $ldapConnection; | 
					
						
							| 
									
										
										
										
											2022-03-24 00:34:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     protected array $config; | 
					
						
							|  |  |  |     protected bool $enabled; | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * LdapService constructor. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-07 06:05:26 +08:00
										 |  |  |     public function __construct(Ldap $ldap, UserAvatars $userAvatars, GroupSyncService $groupSyncService) | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $this->ldap = $ldap; | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  |         $this->userAvatars = $userAvatars; | 
					
						
							| 
									
										
										
										
											2021-10-07 06:05:26 +08:00
										 |  |  |         $this->groupSyncService = $groupSyncService; | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         $this->config = config('services.ldap'); | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  |         $this->enabled = config('auth.method') === 'ldap'; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Check if groups should be synced. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2021-10-07 06:05:26 +08:00
										 |  |  |     public function shouldSyncGroups(): bool | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         return $this->enabled && $this->config['user_to_groups'] !== false; | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |      * Search for attributes for a specific user on the ldap. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      * @throws LdapException | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |     private function getUserWithAttributes(string $userName, array $attributes): ?array | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $ldapConnection = $this->getConnection(); | 
					
						
							|  |  |  |         $this->bindSystemUser($ldapConnection); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-16 06:35:15 +08:00
										 |  |  |         // Clean attributes
 | 
					
						
							|  |  |  |         foreach ($attributes as $index => $attribute) { | 
					
						
							|  |  |  |             if (strpos($attribute, 'BIN;') === 0) { | 
					
						
							|  |  |  |                 $attributes[$index] = substr($attribute, strlen('BIN;')); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         // Find user
 | 
					
						
							|  |  |  |         $userFilter = $this->buildFilter($this->config['user_filter'], ['user' => $userName]); | 
					
						
							|  |  |  |         $baseDn = $this->config['base_dn']; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $followReferrals = $this->config['follow_referrals'] ? 1 : 0; | 
					
						
							|  |  |  |         $this->ldap->setOption($ldapConnection, LDAP_OPT_REFERRALS, $followReferrals); | 
					
						
							|  |  |  |         $users = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $userFilter, $attributes); | 
					
						
							|  |  |  |         if ($users['count'] === 0) { | 
					
						
							|  |  |  |             return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $users[0]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get the details of a user from LDAP using the given username. | 
					
						
							|  |  |  |      * User found via configurable user filter. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      * @throws LdapException | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |     public function getUserDetails(string $userName): ?array | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |         $idAttr = $this->config['id_attribute']; | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         $emailAttr = $this->config['email_attribute']; | 
					
						
							| 
									
										
										
										
											2019-03-09 12:55:11 +08:00
										 |  |  |         $displayNameAttr = $this->config['display_name_attribute']; | 
					
						
							| 
									
										
										
										
											2020-10-13 00:47:36 +08:00
										 |  |  |         $thumbnailAttr = $this->config['thumbnail_attribute']; | 
					
						
							| 
									
										
										
										
											2019-03-09 12:55:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  |         $user = $this->getUserWithAttributes($userName, array_filter([ | 
					
						
							|  |  |  |             'cn', 'dn', $idAttr, $emailAttr, $displayNameAttr, $thumbnailAttr, | 
					
						
							|  |  |  |         ])); | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  |         if (is_null($user)) { | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |             return null; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-10 18:54:19 +08:00
										 |  |  |         $userCn = $this->getUserResponseProperty($user, 'cn', null); | 
					
						
							| 
									
										
										
										
											2020-02-16 04:31:23 +08:00
										 |  |  |         $formatted = [ | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |             'uid'   => $this->getUserResponseProperty($user, $idAttr, $user['dn']), | 
					
						
							|  |  |  |             'name'  => $this->getUserResponseProperty($user, $displayNameAttr, $userCn), | 
					
						
							|  |  |  |             'dn'    => $user['dn'], | 
					
						
							| 
									
										
										
										
											2019-03-10 18:54:19 +08:00
										 |  |  |             'email' => $this->getUserResponseProperty($user, $emailAttr, null), | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  |             'avatar'=> $thumbnailAttr ? $this->getUserResponseProperty($user, $thumbnailAttr, null) : null, | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         ]; | 
					
						
							| 
									
										
										
										
											2020-02-16 04:31:23 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if ($this->config['dump_user_details']) { | 
					
						
							|  |  |  |             throw new JsonDebugException([ | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |                 'details_from_ldap'        => $user, | 
					
						
							| 
									
										
										
										
											2020-02-16 04:31:23 +08:00
										 |  |  |                 'details_bookstack_parsed' => $formatted, | 
					
						
							|  |  |  |             ]); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $formatted; | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-10 18:54:19 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Get a property from an LDAP user response fetch. | 
					
						
							|  |  |  |      * Handles properties potentially being part of an array. | 
					
						
							| 
									
										
										
										
											2020-02-16 04:31:23 +08:00
										 |  |  |      * If the given key is prefixed with 'BIN;', that indicator will be stripped | 
					
						
							|  |  |  |      * from the key and any fetched values will be converted from binary to hex. | 
					
						
							| 
									
										
										
										
											2019-03-10 18:54:19 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     protected function getUserResponseProperty(array $userDetails, string $propertyKey, $defaultValue) | 
					
						
							|  |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-02-16 04:31:23 +08:00
										 |  |  |         $isBinary = strpos($propertyKey, 'BIN;') === 0; | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |         $propertyKey = strtolower($propertyKey); | 
					
						
							| 
									
										
										
										
											2020-02-16 04:31:23 +08:00
										 |  |  |         $value = $defaultValue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($isBinary) { | 
					
						
							|  |  |  |             $propertyKey = substr($propertyKey, strlen('BIN;')); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-10 18:54:19 +08:00
										 |  |  |         if (isset($userDetails[$propertyKey])) { | 
					
						
							| 
									
										
										
										
											2020-02-16 04:31:23 +08:00
										 |  |  |             $value = (is_array($userDetails[$propertyKey]) ? $userDetails[$propertyKey][0] : $userDetails[$propertyKey]); | 
					
						
							|  |  |  |             if ($isBinary) { | 
					
						
							|  |  |  |                 $value = bin2hex($value); | 
					
						
							|  |  |  |             } | 
					
						
							| 
									
										
										
										
											2019-03-10 18:54:19 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-02-16 04:31:23 +08:00
										 |  |  |         return $value; | 
					
						
							| 
									
										
										
										
											2019-03-10 18:54:19 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |      * Check if the given credentials are valid for the given user. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      * @throws LdapException | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2020-02-15 22:44:36 +08:00
										 |  |  |     public function validateUserCredentials(?array $ldapUserDetails, string $password): bool | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2020-02-15 22:44:36 +08:00
										 |  |  |         if (is_null($ldapUserDetails)) { | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |             return false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $ldapConnection = $this->getConnection(); | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         try { | 
					
						
							| 
									
										
										
										
											2020-02-01 19:42:22 +08:00
										 |  |  |             $ldapBind = $this->ldap->bind($ldapConnection, $ldapUserDetails['dn'], $password); | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |         } catch (ErrorException $e) { | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |             $ldapBind = false; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return $ldapBind; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Bind the system user to the LDAP connection using the given credentials | 
					
						
							|  |  |  |      * otherwise anonymous access is attempted. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2021-11-23 07:33:55 +08:00
										 |  |  |      * @param resource $connection | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      * @throws LdapException | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     protected function bindSystemUser($connection) | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         $ldapDn = $this->config['dn']; | 
					
						
							|  |  |  |         $ldapPass = $this->config['pass']; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $isAnonymous = ($ldapDn === false || $ldapPass === false); | 
					
						
							|  |  |  |         if ($isAnonymous) { | 
					
						
							|  |  |  |             $ldapBind = $this->ldap->bind($connection); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             $ldapBind = $this->ldap->bind($connection, $ldapDn, $ldapPass); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (!$ldapBind) { | 
					
						
							|  |  |  |             throw new LdapException(($isAnonymous ? trans('errors.ldap_fail_anonymous') : trans('errors.ldap_fail_authed'))); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Get the connection to the LDAP server. | 
					
						
							|  |  |  |      * Creates a new connection if one does not exist. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      * @throws LdapException | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							|  |  |  |      * @return resource | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      */ | 
					
						
							|  |  |  |     protected function getConnection() | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if ($this->ldapConnection !== null) { | 
					
						
							|  |  |  |             return $this->ldapConnection; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Check LDAP extension in installed
 | 
					
						
							|  |  |  |         if (!function_exists('ldap_connect') && config('app.env') !== 'testing') { | 
					
						
							|  |  |  |             throw new LdapException(trans('errors.ldap_extension_not_installed')); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-08 04:00:04 +08:00
										 |  |  |         // Disable certificate verification.
 | 
					
						
							|  |  |  |         // This option works globally and must be set before a connection is created.
 | 
					
						
							|  |  |  |         if ($this->config['tls_insecure']) { | 
					
						
							|  |  |  |             $this->ldap->setOption(null, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 05:47:53 +08:00
										 |  |  |         $serverDetails = $this->parseServerString($this->config['server']); | 
					
						
							|  |  |  |         $ldapConnection = $this->ldap->connect($serverDetails['host'], $serverDetails['port']); | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if ($ldapConnection === false) { | 
					
						
							|  |  |  |             throw new LdapException(trans('errors.ldap_cannot_connect')); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Set any required options
 | 
					
						
							|  |  |  |         if ($this->config['version']) { | 
					
						
							|  |  |  |             $this->ldap->setVersion($ldapConnection, $this->config['version']); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-02-08 04:00:04 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         // Start and verify TLS if it's enabled
 | 
					
						
							|  |  |  |         if ($this->config['start_tls']) { | 
					
						
							|  |  |  |             $started = $this->ldap->startTls($ldapConnection); | 
					
						
							|  |  |  |             if (!$started) { | 
					
						
							|  |  |  |                 throw new LdapException('Could not start TLS connection'); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         $this->ldapConnection = $ldapConnection; | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         return $this->ldapConnection; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 05:47:53 +08:00
										 |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |      * Parse a LDAP server string and return the host and port for a connection. | 
					
						
							|  |  |  |      * Is flexible to formats such as 'ldap.example.com:8069' or 'ldaps://ldap.example.com'. | 
					
						
							| 
									
										
										
										
											2019-04-17 05:47:53 +08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |     protected function parseServerString(string $serverString): array | 
					
						
							| 
									
										
										
										
											2019-04-17 05:47:53 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $serverNameParts = explode(':', $serverString); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // If we have a protocol just return the full string since PHP will ignore a separate port.
 | 
					
						
							|  |  |  |         if ($serverNameParts[0] === 'ldaps' || $serverNameParts[0] === 'ldap') { | 
					
						
							|  |  |  |             return ['host' => $serverString, 'port' => 389]; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Otherwise, extract the port out
 | 
					
						
							|  |  |  |         $hostName = $serverNameParts[0]; | 
					
						
							|  |  |  |         $ldapPort = (count($serverNameParts) > 1) ? intval($serverNameParts[1]) : 389; | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 05:47:53 +08:00
										 |  |  |         return ['host' => $hostName, 'port' => $ldapPort]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     /** | 
					
						
							|  |  |  |      * Build a filter string by injecting common variables. | 
					
						
							|  |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |     protected function buildFilter(string $filterString, array $attrs): string | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $newAttrs = []; | 
					
						
							|  |  |  |         foreach ($attrs as $key => $attrText) { | 
					
						
							|  |  |  |             $newKey = '${' . $key . '}'; | 
					
						
							| 
									
										
										
										
											2018-12-21 04:04:09 +08:00
										 |  |  |             $newAttrs[$newKey] = $this->ldap->escape($attrText); | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         return strtr($filterString, $newAttrs); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |      * Get the groups a user is a part of on ldap. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  |      * @throws LdapException | 
					
						
							| 
									
										
										
										
											2022-03-24 00:34:23 +08:00
										 |  |  |      * @throws JsonDebugException | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |     public function getUserGroups(string $userName): array | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $groupsAttr = $this->config['group_attribute']; | 
					
						
							|  |  |  |         $user = $this->getUserWithAttributes($userName, [$groupsAttr]); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($user === null) { | 
					
						
							| 
									
										
										
										
											2018-08-12 20:28:40 +08:00
										 |  |  |             return []; | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $userGroups = $this->groupFilter($user); | 
					
						
							| 
									
										
										
										
											2022-03-24 00:34:23 +08:00
										 |  |  |         $allGroups = $this->getGroupsRecursive($userGroups, []); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if ($this->config['dump_user_groups']) { | 
					
						
							|  |  |  |             throw new JsonDebugException([ | 
					
						
							| 
									
										
										
										
											2022-03-25 19:14:27 +08:00
										 |  |  |                 'details_from_ldap'             => $user, | 
					
						
							|  |  |  |                 'parsed_direct_user_groups'     => $userGroups, | 
					
						
							| 
									
										
										
										
											2022-03-24 00:34:23 +08:00
										 |  |  |                 'parsed_recursive_user_groups'  => $allGroups, | 
					
						
							|  |  |  |             ]); | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-10-16 23:01:59 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-24 00:34:23 +08:00
										 |  |  |         return $allGroups; | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |      * Get the parent groups of an array of groups. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  |      * @throws LdapException | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |     private function getGroupsRecursive(array $groupsArray, array $checked): array | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |         $groupsToAdd = []; | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         foreach ($groupsArray as $groupName) { | 
					
						
							|  |  |  |             if (in_array($groupName, $checked)) { | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |             $parentGroups = $this->getGroupGroups($groupName); | 
					
						
							|  |  |  |             $groupsToAdd = array_merge($groupsToAdd, $parentGroups); | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |             $checked[] = $groupName; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |         $groupsArray = array_unique(array_merge($groupsArray, $groupsToAdd), SORT_REGULAR); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (empty($groupsToAdd)) { | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |             return $groupsArray; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return $this->getGroupsRecursive($groupsArray, $checked); | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |      * Get the parent groups of a single group. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  |      * @throws LdapException | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |     private function getGroupGroups(string $groupName): array | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $ldapConnection = $this->getConnection(); | 
					
						
							|  |  |  |         $this->bindSystemUser($ldapConnection); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $followReferrals = $this->config['follow_referrals'] ? 1 : 0; | 
					
						
							|  |  |  |         $this->ldap->setOption($ldapConnection, LDAP_OPT_REFERRALS, $followReferrals); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         $baseDn = $this->config['base_dn']; | 
					
						
							|  |  |  |         $groupsAttr = strtolower($this->config['group_attribute']); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-21 04:04:09 +08:00
										 |  |  |         $groupFilter = 'CN=' . $this->ldap->escape($groupName); | 
					
						
							|  |  |  |         $groups = $this->ldap->searchAndGetEntries($ldapConnection, $baseDn, $groupFilter, [$groupsAttr]); | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         if ($groups['count'] === 0) { | 
					
						
							|  |  |  |             return []; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |         return $this->groupFilter($groups[0]); | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |      * Filter out LDAP CN and DN language in a ldap search return. | 
					
						
							|  |  |  |      * Gets the base CN (common name) of the string. | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |     protected function groupFilter(array $userGroupSearchResponse): array | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |     { | 
					
						
							|  |  |  |         $groupsAttr = strtolower($this->config['group_attribute']); | 
					
						
							|  |  |  |         $ldapGroups = []; | 
					
						
							|  |  |  |         $count = 0; | 
					
						
							| 
									
										
										
										
											2018-12-21 04:04:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if (isset($userGroupSearchResponse[$groupsAttr]['count'])) { | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |             $count = (int) $userGroupSearchResponse[$groupsAttr]['count']; | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-12-21 04:04:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-04-17 05:47:53 +08:00
										 |  |  |         for ($i = 0; $i < $count; $i++) { | 
					
						
							| 
									
										
										
										
											2018-12-21 04:04:09 +08:00
										 |  |  |             $dnComponents = $this->ldap->explodeDn($userGroupSearchResponse[$groupsAttr][$i], 1); | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |             if (!in_array($dnComponents[0], $ldapGroups)) { | 
					
						
							|  |  |  |                 $ldapGroups[] = $dnComponents[0]; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2018-12-21 04:04:09 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  |         return $ldapGroups; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							| 
									
										
										
										
											2019-12-16 20:38:35 +08:00
										 |  |  |      * Sync the LDAP groups to the user roles for the current user. | 
					
						
							| 
									
										
										
										
											2021-06-26 23:23:15 +08:00
										 |  |  |      * | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  |      * @throws LdapException | 
					
						
							| 
									
										
										
										
											2022-03-24 00:34:23 +08:00
										 |  |  |      * @throws JsonDebugException | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  |      */ | 
					
						
							| 
									
										
										
										
											2018-08-19 22:24:42 +08:00
										 |  |  |     public function syncGroups(User $user, string $username) | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  |     { | 
					
						
							| 
									
										
										
										
											2018-08-19 22:24:42 +08:00
										 |  |  |         $userLdapGroups = $this->getUserGroups($username); | 
					
						
							| 
									
										
										
										
											2021-10-07 06:05:26 +08:00
										 |  |  |         $this->groupSyncService->syncUserWithFoundGroups($user, $userLdapGroups, $this->config['remove_from_groups']); | 
					
						
							| 
									
										
										
										
											2018-07-16 01:21:45 +08:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-05-25 01:45:08 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  |     /** | 
					
						
							|  |  |  |      * Save and attach an avatar image, if found in the ldap details, and attach | 
					
						
							|  |  |  |      * to the given user model. | 
					
						
							|  |  |  |      */ | 
					
						
							|  |  |  |     public function saveAndAttachAvatar(User $user, array $ldapUserDetails): void | 
					
						
							|  |  |  |     { | 
					
						
							|  |  |  |         if (is_null(config('services.ldap.thumbnail_attribute')) || is_null($ldapUserDetails['avatar'])) { | 
					
						
							|  |  |  |             return; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         try { | 
					
						
							|  |  |  |             $imageData = $ldapUserDetails['avatar']; | 
					
						
							|  |  |  |             $this->userAvatars->assignToUserFromExistingData($user, $imageData, 'jpg'); | 
					
						
							|  |  |  |         } catch (\Exception $exception) { | 
					
						
							|  |  |  |             Log::info("Failed to use avatar image from LDAP data for user id {$user->id}"); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2018-07-03 01:27:43 +08:00
										 |  |  | } |