Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 165
0.00% covered (danger)
0.00%
0 / 7
CRAP
0.00% covered (danger)
0.00%
0 / 1
ldap
0.00% covered (danger)
0.00%
0 / 165
0.00% covered (danger)
0.00%
0 / 7
1806
0.00% covered (danger)
0.00%
0 / 1
 __construct
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
2
 init
0.00% covered (danger)
0.00%
0 / 32
0.00% covered (danger)
0.00%
0 / 1
182
 login
0.00% covered (danger)
0.00%
0 / 107
0.00% covered (danger)
0.00%
0 / 1
420
 acp
0.00% covered (danger)
0.00%
0 / 3
0.00% covered (danger)
0.00%
0 / 1
2
 get_acp_template
0.00% covered (danger)
0.00%
0 / 13
0.00% covered (danger)
0.00%
0 / 1
6
 ldap_user_filter
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
20
 ldap_escape
0.00% covered (danger)
0.00%
0 / 1
0.00% covered (danger)
0.00%
0 / 1
2
1<?php
2
3/**
4*
5* This file is part of the phpBB Forum Software package.
6*
7* @copyright (c) phpBB Limited <https://www.phpbb.com>
8* @license GNU General Public License, version 2 (GPL-2.0)
9*
10* For full copyright and license information, please see
11* the docs/CREDITS.txt file.
12*
13*/
14
15namespace phpbb\auth\provider;
16
17use phpbb\config\config;
18use phpbb\db\driver\driver_interface;
19use phpbb\language\language;
20use phpbb\user;
21
22/**
23 * Database authentication provider for phpBB3
24 * This is for authentication via the integrated user table
25 */
26class ldap extends base
27{
28    /** @var config phpBB config */
29    protected $config;
30
31    /** @var driver_interface DBAL driver interface */
32    protected $db;
33
34    /** @var language phpBB language class */
35    protected $language;
36
37    /** @var user phpBB user */
38    protected $user;
39
40    /**
41     * LDAP Authentication Constructor
42     *
43     * @param    config                $config        Config object
44     * @param    driver_interface    $db            DBAL driver interface
45     * @param    language            $language    Language object
46     * @param    user                $user        User object
47     */
48    public function __construct(config $config, driver_interface $db, language $language, user $user)
49    {
50        $this->config = $config;
51        $this->db = $db;
52        $this->language = $language;
53        $this->user = $user;
54    }
55
56    /**
57     * {@inheritdoc}
58     */
59    public function init()
60    {
61        if (!@extension_loaded('ldap'))
62        {
63            return $this->language->lang('LDAP_NO_LDAP_EXTENSION');
64        }
65
66        $this->config['ldap_port'] = (int) $this->config['ldap_port'];
67        if ($this->config['ldap_port'])
68        {
69            $ldap = @ldap_connect($this->config['ldap_server'], $this->config['ldap_port']);
70        }
71        else
72        {
73            $ldap = @ldap_connect($this->config['ldap_server']);
74        }
75
76        if (!$ldap)
77        {
78            return $this->language->lang('LDAP_NO_SERVER_CONNECTION');
79        }
80
81        @ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
82        @ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
83
84        if ($this->config['ldap_user'] || $this->config['ldap_password'])
85        {
86            if (!@ldap_bind($ldap, html_entity_decode($this->config['ldap_user'], ENT_COMPAT), html_entity_decode($this->config['ldap_password'], ENT_COMPAT)))
87            {
88                return $this->language->lang('LDAP_INCORRECT_USER_PASSWORD');
89            }
90        }
91
92        // ldap_connect only checks whether the specified server is valid, so the connection might still fail
93        $search = @ldap_search(
94            $ldap,
95            html_entity_decode($this->config['ldap_base_dn'], ENT_COMPAT),
96            $this->ldap_user_filter($this->user->data['username']),
97            (empty($this->config['ldap_email'])) ?
98                array(html_entity_decode($this->config['ldap_uid'], ENT_COMPAT)) :
99                array(html_entity_decode($this->config['ldap_uid'], ENT_COMPAT), html_entity_decode($this->config['ldap_email'], ENT_COMPAT)),
100            0,
101            1
102        );
103
104        if ($search === false)
105        {
106            return $this->language->lang('LDAP_SEARCH_FAILED');
107        }
108
109        $result = @ldap_get_entries($ldap, $search);
110
111        @ldap_close($ldap);
112
113        if (!is_array($result) || count($result) < 2)
114        {
115            return $this->language->lang('LDAP_NO_IDENTITY', $this->user->data['username']);
116        }
117
118        if (!empty($this->config['ldap_email']) && !isset($result[0][html_entity_decode($this->config['ldap_email'])]))
119        {
120            return $this->language->lang('LDAP_NO_EMAIL');
121        }
122
123        return false;
124    }
125
126    /**
127     * {@inheritdoc}
128     */
129    public function login($username, $password)
130    {
131        // do not allow empty password
132        if (!$password)
133        {
134            return array(
135                'status'    => LOGIN_ERROR_PASSWORD,
136                'error_msg'    => 'NO_PASSWORD_SUPPLIED',
137                'user_row'    => array('user_id' => ANONYMOUS),
138            );
139        }
140
141        if (!$username)
142        {
143            return array(
144                'status'    => LOGIN_ERROR_USERNAME,
145                'error_msg'    => 'LOGIN_ERROR_USERNAME',
146                'user_row'    => array('user_id' => ANONYMOUS),
147            );
148        }
149
150        if (!@extension_loaded('ldap'))
151        {
152            return array(
153                'status'        => LOGIN_ERROR_EXTERNAL_AUTH,
154                'error_msg'        => 'LDAP_NO_LDAP_EXTENSION',
155                'user_row'        => array('user_id' => ANONYMOUS),
156            );
157        }
158
159        $this->config['ldap_port'] = (int) $this->config['ldap_port'];
160        if ($this->config['ldap_port'])
161        {
162            $ldap = @ldap_connect($this->config['ldap_server'], $this->config['ldap_port']);
163        }
164        else
165        {
166            $ldap = @ldap_connect($this->config['ldap_server']);
167        }
168
169        if (!$ldap)
170        {
171            return array(
172                'status'        => LOGIN_ERROR_EXTERNAL_AUTH,
173                'error_msg'        => 'LDAP_NO_SERVER_CONNECTION',
174                'user_row'        => array('user_id' => ANONYMOUS),
175            );
176        }
177
178        @ldap_set_option($ldap, LDAP_OPT_PROTOCOL_VERSION, 3);
179        @ldap_set_option($ldap, LDAP_OPT_REFERRALS, 0);
180
181        if ($this->config['ldap_user'] || $this->config['ldap_password'])
182        {
183            if (!@ldap_bind($ldap, html_entity_decode($this->config['ldap_user'], ENT_COMPAT), html_entity_decode($this->config['ldap_password'], ENT_COMPAT)))
184            {
185                return array(
186                    'status'        => LOGIN_ERROR_EXTERNAL_AUTH,
187                    'error_msg'        => 'LDAP_NO_SERVER_CONNECTION',
188                    'user_row'        => array('user_id' => ANONYMOUS),
189                );
190            }
191        }
192
193        $search = @ldap_search(
194            $ldap,
195            html_entity_decode($this->config['ldap_base_dn'], ENT_COMPAT),
196            $this->ldap_user_filter($username),
197            (empty($this->config['ldap_email'])) ?
198                array(html_entity_decode($this->config['ldap_uid'], ENT_COMPAT)) :
199                array(html_entity_decode($this->config['ldap_uid'], ENT_COMPAT), html_entity_decode($this->config['ldap_email'], ENT_COMPAT)),
200            0,
201            1
202        );
203
204        $ldap_result = $search !== false ? @ldap_get_entries($ldap, $search) : [];
205
206        if (is_array($ldap_result) && count($ldap_result) > 1)
207        {
208            if (@ldap_bind($ldap, $ldap_result[0]['dn'], html_entity_decode($password, ENT_COMPAT)))
209            {
210                @ldap_close($ldap);
211
212                $sql ='SELECT user_id, username, user_password, user_passchg, user_email, user_type
213                    FROM ' . USERS_TABLE . "
214                    WHERE username_clean = '" . $this->db->sql_escape(utf8_clean_string($username)) . "'";
215                $result = $this->db->sql_query($sql);
216                $row = $this->db->sql_fetchrow($result);
217                $this->db->sql_freeresult($result);
218
219                if ($row)
220                {
221                    unset($ldap_result);
222
223                    // User inactive...
224                    if ($row['user_type'] == USER_INACTIVE || $row['user_type'] == USER_IGNORE)
225                    {
226                        return array(
227                            'status'        => LOGIN_ERROR_ACTIVE,
228                            'error_msg'        => 'ACTIVE_ERROR',
229                            'user_row'        => $row,
230                        );
231                    }
232
233                    // Successful login... set user_login_attempts to zero...
234                    return array(
235                        'status'        => LOGIN_SUCCESS,
236                        'error_msg'        => false,
237                        'user_row'        => $row,
238                    );
239                }
240                else
241                {
242                    // retrieve default group id
243                    $sql = 'SELECT group_id
244                        FROM ' . GROUPS_TABLE . "
245                        WHERE group_name = '" . $this->db->sql_escape('REGISTERED') . "'
246                            AND group_type = " . GROUP_SPECIAL;
247                    $result = $this->db->sql_query($sql);
248                    $row = $this->db->sql_fetchrow($result);
249                    $this->db->sql_freeresult($result);
250
251                    if (!$row)
252                    {
253                        trigger_error('NO_GROUP');
254                    }
255
256                    // generate user account data
257                    $ldap_user_row = array(
258                        'username'        => $username,
259                        'user_password'    => '',
260                        'user_email'    => (!empty($this->config['ldap_email'])) ? utf8_htmlspecialchars($ldap_result[0][html_entity_decode($this->config['ldap_email'], ENT_COMPAT)][0]) : '',
261                        'group_id'        => (int) $row['group_id'],
262                        'user_type'        => USER_NORMAL,
263                        'user_ip'        => $this->user->ip,
264                        'user_new'        => ($this->config['new_member_post_limit']) ? 1 : 0,
265                    );
266
267                    unset($ldap_result);
268
269                    // this is the user's first login so create an empty profile
270                    return array(
271                        'status'        => LOGIN_SUCCESS_CREATE_PROFILE,
272                        'error_msg'        => false,
273                        'user_row'        => $ldap_user_row,
274                    );
275                }
276            }
277            else
278            {
279                unset($ldap_result);
280                @ldap_close($ldap);
281
282                // Give status about wrong password...
283                return array(
284                    'status'        => LOGIN_ERROR_PASSWORD,
285                    'error_msg'        => 'LOGIN_ERROR_PASSWORD',
286                    'user_row'        => array('user_id' => ANONYMOUS),
287                );
288            }
289        }
290
291        @ldap_close($ldap);
292
293        return array(
294            'status'    => LOGIN_ERROR_USERNAME,
295            'error_msg'    => 'LOGIN_ERROR_USERNAME',
296            'user_row'    => array('user_id' => ANONYMOUS),
297        );
298    }
299
300    /**
301     * {@inheritdoc}
302     */
303    public function acp()
304    {
305        // These are fields required in the config table
306        return array(
307            'ldap_server', 'ldap_port', 'ldap_base_dn', 'ldap_uid', 'ldap_user_filter', 'ldap_email', 'ldap_user', 'ldap_password',
308        );
309    }
310
311    /**
312     * {@inheritdoc}
313     */
314    public function get_acp_template($new_config)
315    {
316        return array(
317            'TEMPLATE_FILE'    => 'auth_provider_ldap.html',
318            'TEMPLATE_VARS'    => array(
319                'AUTH_LDAP_BASE_DN'        => $new_config['ldap_base_dn'],
320                'AUTH_LDAP_EMAIL'        => $new_config['ldap_email'],
321                'AUTH_LDAP_PASSORD'        => $new_config['ldap_password'] !== '' ? '********' : '',
322                'AUTH_LDAP_PORT'        => $new_config['ldap_port'],
323                'AUTH_LDAP_SERVER'        => $new_config['ldap_server'],
324                'AUTH_LDAP_UID'            => $new_config['ldap_uid'],
325                'AUTH_LDAP_USER'        => $new_config['ldap_user'],
326                'AUTH_LDAP_USER_FILTER'    => $new_config['ldap_user_filter'],
327            ),
328        );
329    }
330
331    /**
332     * Generates a filter string for ldap_search to find a user
333     *
334     * @param    $username    string    Username identifying the searched user
335     *
336     * @return                string    A filter string for ldap_search
337     */
338    private function ldap_user_filter($username)
339    {
340        $filter = '(' . $this->config['ldap_uid'] . '=' . $this->ldap_escape(html_entity_decode($username, ENT_COMPAT)) . ')';
341        if ($this->config['ldap_user_filter'])
342        {
343            $_filter = ($this->config['ldap_user_filter'][0] == '(' && substr($this->config['ldap_user_filter'], -1) == ')') ? $this->config['ldap_user_filter'] : "({$this->config['ldap_user_filter']})";
344            $filter = "(&{$filter}{$_filter})";
345        }
346        return $filter;
347    }
348
349    /**
350     * Escapes an LDAP AttributeValue
351     *
352     * @param    string    $string    The string to be escaped
353     * @return    string    The escaped string
354     */
355    private function ldap_escape($string)
356    {
357        return str_replace(array('*', '\\', '(', ')'), array('\\*', '\\\\', '\\(', '\\)'), $string);
358    }
359}