Completed
Branch master (9efafe)
by Patrick
03:38
created

Auth/class.LDAPAuthenticator.php (3 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * LDAPAuthenticator class
4
 *
5
 * This file describes the LDAPAuthenticator class
6
 *
7
 * PHP version 5 and 7
8
 *
9
 * @author Patrick Boyd / [email protected]
10
 * @copyright Copyright (c) 2015, Austin Artistic Reconstruction
11
 * @license http://www.apache.org/licenses/ Apache 2.0 License
12
 */
13
14
namespace Auth;
15
16
/** 
17
 * Sort the provided array by the keys in $orderby 
18
 *
19
 * @param array $array The array to sort
20
 * @param array $orderby An array of keys to sort the array by
21
 */
22
function sort_array(&$array, $orderby)
23
{
24
    $count = count($array);
25
    $keys  = array_keys($orderby);
26
    for($i = 0; $i < $count; $i++)
27
    {
28
        for($j = $i; $j < $count; $j++)
29
        {
30
            $data = strcasecmp($array[$i][$keys[0]][0], $array[$j][$keys[0]][0]);
31
            switch($orderby[$keys[0]])
32
            {
33
                case 1:
34
                    if($data > 0)
35
                    {
36
                        swap($array, $i, $j);
37
                    }
38
                    break;
39
                case 0:
40
                    if($data < 0)
41
                    {
42
                        swap($array, $i, $j);
43
                    }
44
                    break;
45
            }
46
        }
47
    }
48
}
49
50
/**
51
 * Swap two elements of the provided array
52
 *
53
 * @param array $array The array to swap values in
54
 * @param mixed $indexA The key of the first element to swap
55
 * @param mixed $indexB The key of the second element to swap
56
 */
57
function swap(&$array, $indexA, $indexB)
58
{
59
    $tmp = $array[$indexA];
60
    $array[$indexA] = $array[$indexB];
61
    $array[$indexB] = $tmp;
62
}
63
64
/**
65
 * An Authenticator class which uses LDAP as its backend storage mechanism
66
 */
67
class LDAPAuthenticator extends Authenticator
68
{
69
    /** The URL for the LDAP host */
70
    private $host;
71
    /** The base DN for all users in the LDAP server */
72
    public  $user_base;
73
    /** The base DN for all groups in the LDAP server */
74
    public  $group_base;
75
    /** The DN to use to bind if not binding as the user */
76
    private $bindDistinguishedName;
77
    /** The password to use to bind if not binding as the user */
78
    private $bindPassword;
79
80
    /**
81
     * Create an LDAP Authenticator
82
     *
83
     * @param array $params Parementers needed to initialize the authenticator
84
     */
85
    public function __construct($params)
86
    {
87
        parent::__construct($params);
88
        $this->host       = $this->getHostParam($params);
89
        $this->user_base  = $this->getParam($params, 'user_base');
90
        $this->group_base = $this->getParam($params, 'group_base');
91
        $this->bindDistinguishedName = $this->getParam($params, 'bind_dn', '$ldap_auth', 'read_write_pass');
92
        $this->bindPassword = $this->getParam($params, 'bind_pass', '$ldap_auth', 'read_write_user');
93
    }
94
95
    /**
96
     * Return the host string for this authenticator
97
     *
98
     * @param array $params The initial parameters of the authenticator
99
     *
100
     * @return string The host string
101
     *
102
     * @SuppressWarnings("StaticAccess")
103
     */
104
    private function getHostParam($params)
105
    {
106
        if(isset($params['host']))
107
        {
108
            return $params['host'];
109
        }
110
        $settings = \Settings::getInstance();
111
        return $settings->getLDAPSetting('host');
112
    }
113
114
    /**
115
     * Return the required paramter for this authenticator
116
     *
117
     * @param array  $params The initial parameters of the authenticator
118
     * @param string $paramName The name of the parameter in the $paramsArray
119
     * @param string $settingsLocation The location in the Settings class
120
     * @param string $settingsName The name in the Settings class
121
     *
122
     * @return mixed The paramter value
123
     *
124
     * @SuppressWarnings("StaticAccess")
125
     */
126
    private function getParam($params, $paramName, $settingsLocation = '$ldap', $settingsName = false)
127
    {
128
        if($settingsName === false)
129
        {
130
            $settingsName = $paramName;
0 ignored issues
show
Consider using a different name than the parameter $settingsName. This often makes code more readable.
Loading history...
131
        }
132
        if(isset($params[$paramName]))
133
        {
134
            return $params[$paramName];
135
        }
136
        $settings = \Settings::getInstance();
137
        return $settings->getLDAPSetting($settingsName, ($settingsLocation !== '$ldap'));
138
    }
139
140
    /**
141
     * Return an instance to the \LDAP\LDAPServer object instance
142
     *
143
     * @param boolean $bindWrite Should we be able to write to the server?
144
     *
145
     * @return \LDAP\LDAPServer|false The server instance if the binding was successful, otherwise false
146
     *
147
     * @SuppressWarnings("StaticAccess")
148
     */
149
    public function getAndBindServer($bindWrite = false)
150
    {
151
        $server = \LDAP\LDAPServer::getInstance();
152
        $server->user_base = $this->user_base;
153
        $server->group_base = $this->group_base;
154
        $server->connect($this->host);
155
        if($bindWrite === false)
156
        {
157
            $ret = $server->bind();
158
        }
159
        else
160
        {
161
            $ret = $server->bind($this->bindDistinguishedName, $this->bindPassword);
162
        }
163
        if($ret === false)
164
        {
165
            return false;
166
        }
167
        return $server;
168
    }
169
170
    /**
171
     * Log the user in provided the credetials
172
     *
173
     * @param string $username The UID or email address for the user
174
     * @param string $password The password for the user
175
     *
176
     * @return mixed False if the login failed and array otherwise
177
     */
178
    public function login($username, $password)
179
    {
180
        $server = $this->getAndBindServer();
181
        if($server === false)
182
        {
183
            return false;
184
        }
185
        $filter = new \Data\Filter("uid eq $username or mail eq $username");
186
        $user = $server->read($this->user_base, $filter);
187
        if($user === false || count($user) === 0)
188
        {
189
            return false;
190
        }
191
        $user = $user[0];
192
        $server->unbind();
193
        $ret = $server->bind($user->dn, $password);
194
        if($ret !== false)
195
        {
196
            return array('res'=>true, 'extended'=>$user); 
197
        }
198
        return false;
199
    }
200
201
    /**
202
     * Does this array represent a successful login?
203
     *
204
     * @param array $data The array data stored in the session after a login call
205
     *
206
     * @return boolean True if the user is logged in, false otherwise
207
     */
208
    public function isLoggedIn($data)
209
    {
210
        if(isset($data['res']))
211
        {
212
            return $data['res'];
213
        }
214
        return false;
215
    }
216
217
    /**
218
     * Obtain the currently logged in user from the session data
219
     *
220
     * @param \stdClass	$data The AuthData from the session
221
     *
222
     * @return null|\Auth\LDAPUser The LDAPUser represented by this data
223
     */
224
    public function getUser($data)
225
    {
226
        return new LDAPUser($data);
227
    }
228
229
    /**
230
     * Obtain the group based on the group name
231
     *
232
     * @param string $name The Group's name
233
     *
234
     * @return null|\Auth\LDAPGroup The LDAPGroup represented by the name or null if not found
235
     */
236
    public function getGroupByName($name)
237
    {
238
        $server = $this->getAndBindServer();
239
        if($server === false)
240
        {
241
            return null;
242
        }
243
        return LDAPGroup::from_name($name, $server);
244
    }
245
246
    public function getGroupsByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false)
247
    {
248
        $server = $this->getAndBindServer();
249
        if($server === false)
250
        {
251
            return false;
252
        }
253
        if($filter === false)
254
        {
255
            $filter = new \Data\Filter('cn eq *');
0 ignored issues
show
Consider using a different name than the parameter $filter. This often makes code more readable.
Loading history...
256
        }
257
        $groups = $server->read($this->group_base, $filter);
258
        if($groups === false)
259
        {
260
            return false;
261
        }
262
        $this->processFilteringParams($groups, $select, $top, $skip, $orderby);
263
        $count = count($groups);
264
        for($i = 0; $i < $count; $i++)
265
        {
266
            $groups[$i] = new LDAPGroup($groups[$i]);
267
            if($select !== false)
268
            {
269
                $groups[$i] = json_decode(json_encode($groups[$i]), true);
270
                $groups[$i] = array_intersect_key($groups[$i], array_flip($select));
271
            }
272
        }
273
        return $groups;
274
    }
275
276
    public function getActiveUserCount()
277
    {
278
        $server = $this->getAndBindServer();
279
        if($server === false)
280
        {
281
            return 0;
282
        }
283
        return $server->count($this->user_base);
284
    }
285
286
    /**
287
     * @param array           $data The array data to filter and sort
288
     * @param boolean|array   $select The fields to return
289
     * @param boolean|integer $top The number of records to return
290
     * @param boolean|integer $skip The number of records to skip
291
     * @param boolean|array   $orderby The fields to sort by
292
     */
293
    private function processFilteringParams(&$data, &$select, $top, $skip, $orderby)
294
    {
295
        if($orderby !== false)
296
        {
297
            sort_array($data, $orderby);
298
        }
299
        if($select !== false)
300
        {
301
            $select = array_flip($select);
302
        }
303
        if($skip !== false && $top !== false)
304
        {
305
            $data = array_slice($data, $skip, $top);
306
        }
307
        else if($top !== false)
308
        {
309
            $data = array_slice($data, 0, $top);
310
        }
311
        else if($skip !== false)
312
        {
313
            $data = array_slice($data, $skip);
314
        }
315
    }
316
317
    
318
    /**
319
     * @param boolean|\Data\Filter $filter The filter to user when reading users
320
     * @param boolean|array   $select The fields to return
321
     * @param boolean|integer $top The number of records to return
322
     * @param boolean|integer $skip The number of records to skip
323
     * @param boolean|array   $orderby The fields to sort by
324
     *
325
     * @return array|boolean False if no users found, an array of user objects otherwise
326
     */
327
    public function getUsersByFilter($filter, $select = false, $top = false, $skip = false, $orderby = false)
328
    {
329
        $server = $this->getAndBindServer();
330
        if($server === false)
331
        {
332
            return false;
333
        }
334
        if($filter === false)
335
        {
336
            $filter = new \Data\Filter('cn eq *');
0 ignored issues
show
Consider using a different name than the parameter $filter. This often makes code more readable.
Loading history...
337
        }
338
        $users = $server->read($this->user_base, $filter, false, $select);
339
        if($users === false)
340
        {
341
            return false;
342
        }
343
        $this->processFilteringParams($users, $select, $top, $skip, $orderby);
344
        $count = count($users);
345
        for($i = 0; $i < $count; $i++)
346
        {
347
            $tmp = new LDAPUser($users[$i]);
348
            if($select !== false)
349
            {
350
                $tmp = $tmp->jsonSerialize();
351
                $tmp = array_intersect_key($tmp, $select);
352
            }
353
            $users[$i] = $tmp;
354
        }
355
        return $users;
356
    }
357
358
    public function activatePendingUser($user)
359
    {
360
        $this->getAndBindServer(true);
361
        $newUser = new LDAPUser();
362
        $newUser->uid = $user->uid;
363
        $newUser->mail = $user->mail;
364
        $newUser->sn = $user->sn;
365
        $newUser->givenName = $user->givenName;
366
        $newUser->host = $user->host;
367
        $pass = $user->getPassword();
368
        if($pass !== false)
369
        {
370
            $newUser->setPass($pass);
371
        }
372
        $ret = $newUser->flushUser();
373
        if($ret)
374
        {
375
            $user->delete();
376
        }
377
        $users = $this->getUsersByFilter(new \Data\Filter('mail eq '.$user->mail));
378
        if(empty($users))
379
        {
380
            throw new \Exception('Error creating user!');
381
        }
382
        return $users[0];
383
    }
384
385
    public function getUserByResetHash($hash)
386
    {
387
        $users = $this->getUsersByFilter(new \Data\Filter("uniqueIdentifier eq $hash"));
388
        if($users === false || !isset($users[0]))
389
        {
390
            return false;
391
        }
392
        return $users[0];
393
    }
394
}
395
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
396