Rev 77 | Only display areas with differences | Regard whitespace | Details | Blame | Last modification | View Log | RSS feed
Rev 77 | Rev 89 | ||
---|---|---|---|
1 | <?php |
1 | <?php |
2 | 2 | ||
3 | /* |
3 | /* |
4 | * VtsLDAPUtils - Simple LDAP helper functions |
4 | * VtsLDAPUtils - Simple LDAP helper functions |
5 | * Copyright 2021 - 2023 Daniel Marschall, ViaThinkSoft |
5 | * Copyright 2021 - 2023 Daniel Marschall, ViaThinkSoft |
6 | * Revision: 2023-04-09 |
6 | * Revision: 2023-11-30 |
7 | * |
7 | * |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
8 | * Licensed under the Apache License, Version 2.0 (the "License"); |
9 | * you may not use this file except in compliance with the License. |
9 | * you may not use this file except in compliance with the License. |
10 | * You may obtain a copy of the License at |
10 | * You may obtain a copy of the License at |
11 | * |
11 | * |
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
12 | * http://www.apache.org/licenses/LICENSE-2.0 |
13 | * |
13 | * |
14 | * Unless required by applicable law or agreed to in writing, software |
14 | * Unless required by applicable law or agreed to in writing, software |
15 | * distributed under the License is distributed on an "AS IS" BASIS, |
15 | * distributed under the License is distributed on an "AS IS" BASIS, |
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
16 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
17 | * See the License for the specific language governing permissions and |
17 | * See the License for the specific language governing permissions and |
18 | * limitations under the License. |
18 | * limitations under the License. |
19 | */ |
19 | */ |
20 | 20 | ||
21 | class VtsLDAPUtils { |
21 | class VtsLDAPUtils { |
22 | 22 | ||
23 | protected $conn = null; |
23 | protected $conn = null; |
24 | 24 | ||
25 | private static function _L(string $str, ...$sprintfArgs): string { |
25 | private static function _L(string $str, ...$sprintfArgs): string { |
26 | if (function_exists('_L')) { |
26 | if (function_exists('_L')) { |
27 | return _L($str, $sprintfArgs); |
27 | return _L($str, $sprintfArgs); |
28 | } else if (function_exists('my_vsprintf')) { |
28 | } else if (function_exists('my_vsprintf')) { |
29 | return my_vsprintf($str, $sprintfArgs); |
29 | return my_vsprintf($str, $sprintfArgs); |
30 | } else { |
30 | } else { |
31 | $n = 1; |
31 | $n = 1; |
32 | foreach ($sprintfArgs as $val) { |
32 | foreach ($sprintfArgs as $val) { |
33 | $str = str_replace("%$n", $val, $str); |
33 | $str = str_replace("%$n", $val, $str); |
34 | $n++; |
34 | $n++; |
35 | } |
35 | } |
36 | $str = str_replace("%%", "%", $str); |
36 | $str = str_replace("%%", "%", $str); |
37 | return $str; |
37 | return $str; |
38 | } |
38 | } |
39 | } |
39 | } |
40 | 40 | ||
41 | public static function getString($ldap_userinfo, $attributeName) { |
41 | public static function getString($ldap_userinfo, $attributeName) { |
42 | $ary = self::getArray($ldap_userinfo, $attributeName); |
42 | $ary = self::getArray($ldap_userinfo, $attributeName); |
43 | return implode("\n", $ary); |
43 | return implode("\n", $ary); |
44 | } |
44 | } |
45 | 45 | ||
46 | public static function getArray($ldap_userinfo, $attributeName) { |
46 | public static function getArray($ldap_userinfo, $attributeName) { |
47 | $ary = array(); |
47 | $ary = array(); |
48 | if (isset($ldap_userinfo[$attributeName])) { |
48 | if (isset($ldap_userinfo[$attributeName])) { |
49 | $cnt = $ldap_userinfo[$attributeName]['count']; |
49 | $cnt = $ldap_userinfo[$attributeName]['count']; |
50 | for ($i=0; $i<$cnt; $i++) { |
50 | for ($i=0; $i<$cnt; $i++) { |
51 | $ary[] = $ldap_userinfo[$attributeName][$i]; |
51 | $ary[] = $ldap_userinfo[$attributeName][$i]; |
52 | } |
52 | } |
53 | } |
53 | } |
54 | return $ary; |
54 | return $ary; |
55 | } |
55 | } |
56 | 56 | ||
57 | public function isMemberOfRec($userDN, $groupDN) { |
57 | public function isMemberOfRec($userDN, $groupDN) { |
58 | 58 | ||
59 | if (isset($userDN['dn'])) $userDN = $userDN['dn']; |
59 | if (isset($userDN['dn'])) $userDN = $userDN['dn']; |
60 | if (isset($groupDN['dn'])) $groupDN = $groupDN['dn']; |
60 | if (isset($groupDN['dn'])) $groupDN = $groupDN['dn']; |
61 | 61 | ||
62 | if (!$this->conn) throw new Exception('LDAP not connected'); |
62 | if (!$this->conn) throw new Exception('LDAP not connected'); |
63 | $res = ldap_read($this->conn, $groupDN, "(objectClass=*)"); |
63 | $res = ldap_read($this->conn, $groupDN, "(objectClass=*)"); |
64 | if (!$res) return false; |
64 | if (!$res) return false; |
65 | $entries = ldap_get_entries($this->conn, $res); |
65 | $entries = ldap_get_entries($this->conn, $res); |
66 | if (!isset($entries[0])) return false; |
66 | if (!isset($entries[0])) return false; |
67 | if (!isset($entries[0]['member'])) return false; |
67 | if (!isset($entries[0]['member'])) return false; |
68 | if (!isset($entries[0]['member']['count'])) return false; |
68 | if (!isset($entries[0]['member']['count'])) return false; |
69 | $cntMember = $entries[0]['member']['count']; |
69 | $cntMember = $entries[0]['member']['count']; |
70 | for ($iMember=0; $iMember<$cntMember; $iMember++) { |
70 | for ($iMember=0; $iMember<$cntMember; $iMember++) { |
71 | $groupOrUser = $entries[0]['member'][$iMember]; |
71 | $groupOrUser = $entries[0]['member'][$iMember]; |
72 | if (strtolower($groupOrUser) == strtolower($userDN)) return true; |
72 | if (strtolower($groupOrUser) == strtolower($userDN)) return true; |
73 | if ($this->isMemberOfRec($userDN, $groupOrUser)) return true; |
73 | if ($this->isMemberOfRec($userDN, $groupOrUser)) return true; |
74 | } |
74 | } |
75 | return false; |
75 | return false; |
76 | } |
76 | } |
77 | 77 | ||
78 | public function __destruct() { |
78 | public function __destruct() { |
79 | $this->disconnect(); |
79 | $this->disconnect(); |
80 | } |
80 | } |
81 | 81 | ||
82 | public function disconnect() { |
82 | public function disconnect() { |
83 | if ($this->conn) { |
83 | if ($this->conn) { |
84 | //ldap_unbind($this->conn); // commented out because ldap_unbind() kills the link descriptor |
84 | //ldap_unbind($this->conn); // commented out because ldap_unbind() kills the link descriptor |
85 | ldap_close($this->conn); |
85 | ldap_close($this->conn); |
86 | $this->conn = null; |
86 | $this->conn = null; |
87 | } |
87 | } |
88 | } |
88 | } |
89 | 89 | ||
90 | public function connect($cfg_ldap_server, $cfg_ldap_port) { |
90 | public function connect($cfg_ldap_server, $cfg_ldap_port=389) { |
91 | $this->disconnect(); |
91 | $this->disconnect(); |
92 | 92 | ||
93 | // Connect to the server |
93 | // Connect to the server |
94 | if (!empty($cfg_ldap_port)) { |
94 | if (strpos($cfg_ldap_server, '://') !== false) { |
95 | if (!($ldapconn = @ldap_connect($cfg_ldap_server, $cfg_ldap_port))) throw new Exception(self::_L('Cannot connect to LDAP server')); |
95 | // e.g. ldap://hostname:port or ldaps://hostname:port |
- | 96 | $uri = $cfg_ldap_server; |
|
96 | } else { |
97 | } else { |
97 | if (!($ldapconn = @ldap_connect($cfg_ldap_server))) throw new Exception(self::_L('Cannot connect to LDAP server')); |
98 | $secure = ($cfg_ldap_port == 636) || ($cfg_ldap_port == 3268) || ($cfg_ldap_port == 3269); |
- | 99 | $schema = $secure ? 'ldaps' : 'ldap'; |
|
- | 100 | $uri = $schema . '://' . $cfg_ldap_server . ':' . $cfg_ldap_port; |
|
98 | } |
101 | } |
- | 102 | if (!($ldapconn = @ldap_connect($uri))) throw new Exception(self::_L('Cannot connect to LDAP server')); |
|
- | 103 | ||
99 | ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); |
104 | ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); |
100 | ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0); |
105 | ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0); |
101 | 106 | ||
102 | $this->conn = $ldapconn; |
107 | $this->conn = $ldapconn; |
103 | } |
108 | } |
104 | 109 | ||
105 | public function login($username, $password) { |
110 | public function login($username, $password) { |
106 | return @ldap_bind($this->conn, $username, $password); |
111 | return @ldap_bind($this->conn, $username, $password); |
107 | } |
112 | } |
108 | 113 | ||
109 | public function getUserInfo($userPrincipalName, $cfg_ldap_base_dn) { |
114 | public function getUserInfo($userPrincipalName, $cfg_ldap_base_dn) { |
110 | $cfg_ldap_user_filter = "(&(objectClass=user)(objectCategory=person)(userPrincipalName=".ldap_escape($userPrincipalName, '', LDAP_ESCAPE_FILTER)."))"; |
115 | $cfg_ldap_user_filter = "(&(objectClass=user)(objectCategory=person)(userPrincipalName=".ldap_escape($userPrincipalName, '', LDAP_ESCAPE_FILTER)."))"; |
111 | 116 | ||
112 | if (!($result = @ldap_search($this->conn,$cfg_ldap_base_dn, $cfg_ldap_user_filter))) throw new Exception(self::_L('Error in search query: %1', ldap_error($this->conn))); |
117 | if (!($result = @ldap_search($this->conn,$cfg_ldap_base_dn, $cfg_ldap_user_filter))) throw new Exception(self::_L('Error in search query: %1', ldap_error($this->conn))); |
113 | $data = ldap_get_entries($this->conn, $result); |
118 | $data = ldap_get_entries($this->conn, $result); |
114 | $ldap_userinfo = array(); |
119 | $ldap_userinfo = array(); |
115 | 120 | ||
116 | if ($data['count'] == 0) return false; /* @phpstan-ignore-line */ |
121 | if ($data['count'] == 0) return false; /* @phpstan-ignore-line */ |
117 | $ldap_userinfo = $data[0]; |
122 | $ldap_userinfo = $data[0]; |
118 | 123 | ||
119 | // empty($ldap_userinfo) can happen if the user did not log-in using their correct userPrincipalName (e.g. "username@domainname" instead of "username@domainname.local") |
124 | // empty($ldap_userinfo) can happen if the user did not log-in using their correct userPrincipalName (e.g. "username@domainname" instead of "username@domainname.local") |
120 | return empty($ldap_userinfo) ? false : $ldap_userinfo; |
125 | return empty($ldap_userinfo) ? false : $ldap_userinfo; |
121 | } |
126 | } |
122 | 127 | ||
123 | } |
128 | } |
124 | 129 |