1 | <?php |
||
2 | /* |
||
3 | * This program is free software: you can redistribute it and/or modify |
||
4 | * it under the terms of the GNU General Public License as published by |
||
5 | * the Free Software Foundation, either version 3 of the License, or |
||
6 | * (at your option) any later version. |
||
7 | * |
||
8 | * This program is distributed in the hope that it will be useful, |
||
9 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
||
10 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the |
||
11 | * GNU General Public License for more details. |
||
12 | * |
||
13 | * You should have received a copy of the GNU General Public License |
||
14 | * along with this program. If not, see <https://www.gnu.org/licenses/>. |
||
15 | */ |
||
16 | |||
17 | /** |
||
18 | * libreNMS HTTP-Authentication and LDAP Authorization Library |
||
19 | * |
||
20 | * @author Maximilian Wilhelm <[email protected]> |
||
21 | * @copyright 2016 LibreNMS, Barbarossa |
||
22 | * @license GPL |
||
23 | */ |
||
24 | |||
25 | namespace LibreNMS\Authentication; |
||
26 | |||
27 | use App\Models\User; |
||
28 | use LibreNMS\Config; |
||
29 | use LibreNMS\Exceptions\AuthenticationException; |
||
30 | use LibreNMS\Exceptions\LdapMissingException; |
||
31 | |||
32 | class LdapAuthorizationAuthorizer extends AuthorizerBase |
||
33 | { |
||
34 | use LdapSessionCache; |
||
35 | |||
36 | protected $ldap_connection; |
||
37 | protected static $AUTH_IS_EXTERNAL = true; |
||
38 | |||
39 | public function __construct() |
||
40 | { |
||
41 | if (! function_exists('ldap_connect')) { |
||
42 | throw new LdapMissingException(); |
||
43 | } |
||
44 | |||
45 | /** |
||
46 | * Set up connection to LDAP server |
||
47 | */ |
||
48 | $this->ldap_connection = @ldap_connect(Config::get('auth_ldap_server'), Config::get('auth_ldap_port')); |
||
49 | if (! $this->ldap_connection) { |
||
50 | throw new AuthenticationException('Fatal error while connecting to LDAP server ' . Config::get('auth_ldap_server') . ':' . Config::get('auth_ldap_port') . ': ' . ldap_error($this->ldap_connection)); |
||
51 | } |
||
52 | if (Config::get('auth_ldap_version')) { |
||
53 | ldap_set_option($this->ldap_connection, LDAP_OPT_PROTOCOL_VERSION, Config::get('auth_ldap_version')); |
||
54 | } |
||
55 | |||
56 | if (Config::get('auth_ldap_starttls') && (Config::get('auth_ldap_starttls') == 'optional' || Config::get('auth_ldap_starttls') == 'require')) { |
||
57 | $tls = ldap_start_tls($this->ldap_connection); |
||
58 | if (Config::get('auth_ldap_starttls') == 'require' && $tls === false) { |
||
59 | throw new AuthenticationException('Fatal error: LDAP TLS required but not successfully negotiated:' . ldap_error($this->ldap_connection)); |
||
60 | } |
||
61 | } |
||
62 | } |
||
63 | |||
64 | public function authenticate($credentials) |
||
65 | { |
||
66 | if (isset($credentials['username']) && $this->userExists($credentials['username'])) { |
||
67 | return true; |
||
68 | } |
||
69 | |||
70 | $guest = Config::get('http_auth_guest'); |
||
71 | if ($guest && User::thisAuth()->where('username', $guest)->exists()) { |
||
72 | return true; |
||
73 | } |
||
74 | |||
75 | throw new AuthenticationException(); |
||
76 | } |
||
77 | |||
78 | public function userExists($username, $throw_exception = false) |
||
79 | { |
||
80 | if ($this->authLdapSessionCacheGet('user_exists')) { |
||
81 | return true; |
||
82 | } |
||
83 | |||
84 | $filter = '(' . Config::get('auth_ldap_prefix') . $username . ')'; |
||
85 | $search = ldap_search($this->ldap_connection, trim(Config::get('auth_ldap_suffix'), ','), $filter); |
||
86 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||
87 | if ($entries['count']) { |
||
88 | /* |
||
89 | * Cache positiv result as this will result in more queries which we |
||
90 | * want to speed up. |
||
91 | */ |
||
92 | $this->authLdapSessionCacheSet('user_exists', 1); |
||
93 | |||
94 | return true; |
||
95 | } |
||
96 | |||
97 | /* |
||
98 | * Don't cache that user doesn't exists as this might be a misconfiguration |
||
99 | * on some end and the user will be happy if it "just works" after the user |
||
100 | * has been added to LDAP. |
||
101 | */ |
||
102 | return false; |
||
103 | } |
||
104 | |||
105 | public function getUserlevel($username) |
||
106 | { |
||
107 | $userlevel = $this->authLdapSessionCacheGet('userlevel'); |
||
108 | if ($userlevel) { |
||
109 | return $userlevel; |
||
110 | } else { |
||
111 | $userlevel = 0; |
||
112 | } |
||
113 | |||
114 | // Find all defined groups $username is in |
||
115 | $filter = '(&(|(cn=' . join(')(cn=', array_keys(Config::get('auth_ldap_groups'))) . '))(' . Config::get('auth_ldap_groupmemberattr') . '=' . $this->getMembername($username) . '))'; |
||
116 | $search = ldap_search($this->ldap_connection, Config::get('auth_ldap_groupbase'), $filter); |
||
117 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||
118 | |||
119 | // Loop the list and find the highest level |
||
120 | foreach ($entries as $entry) { |
||
121 | $groupname = $entry['cn'][0]; |
||
122 | $authLdapGroups = Config::get('auth_ldap_groups'); |
||
123 | if ($authLdapGroups[$groupname]['level'] > $userlevel) { |
||
124 | $userlevel = $authLdapGroups[$groupname]['level']; |
||
125 | } |
||
126 | } |
||
127 | |||
128 | $this->authLdapSessionCacheSet('userlevel', $userlevel); |
||
129 | |||
130 | return $userlevel; |
||
131 | } |
||
132 | |||
133 | public function getUserid($username) |
||
134 | { |
||
135 | $user_id = $this->authLdapSessionCacheGet('userid'); |
||
136 | if (isset($user_id)) { |
||
137 | return $user_id; |
||
138 | } |
||
139 | |||
140 | $guest_username = Config::get('http_auth_guest'); |
||
141 | $user_id = User::thisAuth()->where('username', $guest_username)->value('auth_id') ?: -1; |
||
142 | |||
143 | $filter = '(' . Config::get('auth_ldap_prefix') . $username . ')'; |
||
144 | $search = ldap_search($this->ldap_connection, trim(Config::get('auth_ldap_suffix'), ','), $filter); |
||
145 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||
146 | |||
147 | if ($entries['count']) { |
||
148 | $user_id = (int) $entries[0]['uidnumber'][0]; |
||
149 | } |
||
150 | |||
151 | if ($user_id === -1) { |
||
152 | // no user or guest user, don't allow |
||
153 | if ($guest_username) { |
||
154 | throw new AuthenticationException(); |
||
155 | } else { |
||
156 | throw new AuthenticationException('Guest login allowed.'); |
||
157 | } |
||
158 | } |
||
159 | |||
160 | $this->authLdapSessionCacheSet('userid', $user_id); |
||
161 | |||
162 | return $user_id; |
||
0 ignored issues
–
show
Bug
Best Practice
introduced
by
Loading history...
|
|||
163 | } |
||
164 | |||
165 | public function getUserlist() |
||
166 | { |
||
167 | $userlist = []; |
||
168 | |||
169 | $filter = '(' . Config::get('auth_ldap_prefix') . '*)'; |
||
170 | |||
171 | $search = ldap_search($this->ldap_connection, trim(Config::get('auth_ldap_suffix'), ','), $filter); |
||
172 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||
173 | |||
174 | if ($entries['count']) { |
||
175 | foreach ($entries as $entry) { |
||
176 | $username = $entry['uid'][0]; |
||
177 | $realname = $entry['cn'][0]; |
||
178 | $user_id = $entry['uidnumber'][0]; |
||
179 | $email = $entry[Config::get('auth_ldap_emailattr')][0]; |
||
180 | $ldap_groups = $this->getGroupList(); |
||
181 | foreach ($ldap_groups as $ldap_group) { |
||
182 | $ldap_comparison = ldap_compare( |
||
183 | $this->ldap_connection, |
||
184 | $ldap_group, |
||
185 | Config::get('auth_ldap_groupmemberattr'), |
||
186 | $this->getMembername($username) |
||
187 | ); |
||
188 | if (! Config::has('auth_ldap_group') || $ldap_comparison === true) { |
||
189 | $userlist[] = [ |
||
190 | 'username' => $username, |
||
191 | 'realname' => $realname, |
||
192 | 'user_id' => $user_id, |
||
193 | 'email' => $email, |
||
194 | ]; |
||
195 | } |
||
196 | } |
||
197 | } |
||
198 | } |
||
199 | |||
200 | return $userlist; |
||
201 | } |
||
202 | |||
203 | public function getUser($user_id) |
||
204 | { |
||
205 | foreach ($this->getUserlist() as $user) { |
||
206 | if ((int) $user['user_id'] === (int) $user_id) { |
||
207 | $user['level'] = $this->getUserlevel($user['username']); |
||
208 | |||
209 | return $user; |
||
210 | } |
||
211 | } |
||
212 | |||
213 | return false; |
||
214 | } |
||
215 | |||
216 | protected function getMembername($username) |
||
217 | { |
||
218 | if (Config::get('auth_ldap_groupmembertype') == 'fulldn') { |
||
219 | $membername = Config::get('auth_ldap_prefix') . $username . Config::get('auth_ldap_suffix'); |
||
220 | } elseif (Config::get('auth_ldap_groupmembertype') == 'puredn') { |
||
221 | $filter = '(' . Config::get('auth_ldap_attr.uid') . '=' . $username . ')'; |
||
222 | $search = ldap_search($this->ldap_connection, Config::get('auth_ldap_groupbase'), $filter); |
||
223 | $entries = ldap_get_entries($this->ldap_connection, $search); |
||
224 | $membername = $entries[0]['dn']; |
||
225 | } else { |
||
226 | $membername = $username; |
||
227 | } |
||
228 | |||
229 | return $membername; |
||
230 | } |
||
231 | |||
232 | public function getGroupList() |
||
233 | { |
||
234 | $ldap_groups = []; |
||
235 | $default_group = 'cn=groupname,ou=groups,dc=example,dc=com'; |
||
236 | if (Config::has('auth_ldap_group')) { |
||
237 | if (Config::get('auth_ldap_group') !== $default_group) { |
||
238 | $ldap_groups[] = Config::get('auth_ldap_group'); |
||
239 | } |
||
240 | } |
||
241 | |||
242 | foreach (Config::get('auth_ldap_groups') as $key => $value) { |
||
243 | $dn = "cn=$key," . Config::get('auth_ldap_groupbase'); |
||
244 | $ldap_groups[] = $dn; |
||
245 | } |
||
246 | |||
247 | return $ldap_groups; |
||
248 | } |
||
249 | } |
||
250 |