Dotclear

source: inc/core/class.dc.auth.php @ 3340:30cec05f4e63

Revision 3340:30cec05f4e63, 14.8 KB checked in by franck <carnet.franck.paul@…>, 9 years ago (diff)

PHP 5.3+ : (expr1 ? expr1 : expr2) may be written → (expr1 ?: expr2)

Line 
1<?php
2# -- BEGIN LICENSE BLOCK ---------------------------------------
3#
4# This file is part of Dotclear 2.
5#
6# Copyright (c) 2003-2013 Olivier Meunier & Association Dotclear
7# Licensed under the GPL version 2.0 license.
8# See LICENSE file or
9# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
10#
11# -- END LICENSE BLOCK -----------------------------------------
12if (!defined('DC_RC_PATH')) { return; }
13
14/**
15* @ingroup DC_CORE
16* @nosubgrouping
17* @brief Authentication and user credentials management
18*
19* dcAuth is a class used to handle everything related to user authentication
20* and credentials. Object is provided by dcCore $auth property.
21*/
22class dcAuth
23{
24     /** @var dcCore dcCore instance */
25     protected $core;
26     /** @var connection Database connection object */
27     protected $con;
28
29     /** @var string User table name */
30     protected $user_table;
31     /** @var string Perm table name */
32     protected $perm_table;
33
34     /** @var string Current user ID */
35     protected $user_id;
36     /** @var array Array with user information */
37     protected $user_info = array();
38     /** @var array Array with user options */
39     protected $user_options = array();
40     /** @var boolean User must change his password after login */
41     protected $user_change_pwd;
42     /** @var boolean User is super admin */
43     protected $user_admin;
44     /** @var array Permissions for each blog */
45     protected $permissions = array();
46     /** @var boolean User can change its password */
47     protected $allow_pass_change = true;
48     /** @var array List of blogs on which the user has permissions */
49     protected $blogs = array();
50     /** @var integer Count of user blogs */
51     public $blog_count = null;
52
53     /** @var array Permission types */
54     protected $perm_types;
55
56     /** @var dcPrefs dcPrefs object */
57     public $user_prefs;
58
59     /**
60     * Class constructor. Takes dcCore object as single argument.
61     *
62     * @param dcCore     $core          dcCore object
63     */
64     public function __construct($core)
65     {
66          $this->core =& $core;
67          $this->con =& $core->con;
68          $this->blog_table = $core->prefix.'blog';
69          $this->user_table = $core->prefix.'user';
70          $this->perm_table = $core->prefix.'permissions';
71
72          $this->perm_types = array(
73               'admin' => __('administrator'),
74               'usage' => __('manage their own entries and comments'),
75               'publish' => __('publish entries and comments'),
76               'delete' => __('delete entries and comments'),
77               'contentadmin' => __('manage all entries and comments'),
78               'categories' => __('manage categories'),
79               'media' => __('manage their own media items'),
80               'media_admin' => __('manage all media items')
81          );
82     }
83
84     /// @name Credentials and user permissions
85     //@{
86     /**
87     * Checks if user exists and can log in. <var>$pwd</var> argument is optionnal
88     * while you may need to check user without password. This method will create
89     * credentials and populate all needed object properties.
90     *
91     * @param string     $user_id       User ID
92     * @param string     $pwd           User password
93     * @param string     $user_key      User key check
94     * @param boolean    $check_blog    checks if user is associated to a blog or not.
95     * @return boolean
96     */
97     public function checkUser($user_id, $pwd=null, $user_key=null, $check_blog=true)
98     {
99          # Check user and password
100          $strReq = 'SELECT user_id, user_super, user_pwd, user_change_pwd, '.
101                    'user_name, user_firstname, user_displayname, user_email, '.
102                    'user_url, user_default_blog, user_options, '.
103                    'user_lang, user_tz, user_post_status, user_creadt, user_upddt '.
104                    'FROM '.$this->con->escapeSystem($this->user_table).' '.
105                    "WHERE user_id = '".$this->con->escape($user_id)."' ";
106
107          try {
108               $rs = $this->con->select($strReq);
109          } catch (Exception $e) {
110               $err = $e->getMessage();
111               return false;
112          }
113
114          if ($rs->isEmpty()) {
115               sleep(rand(2,5));
116               return false;
117          }
118
119          $rs->extend('rsExtUser');
120
121          if ($pwd != '')
122          {
123               if ($this->crypt($pwd) != $rs->user_pwd) {
124                    sleep(rand(2,5));
125                    return false;
126               }
127          }
128          elseif ($user_key != '')
129          {
130               if (http::browserUID(DC_MASTER_KEY.$rs->user_id.$rs->user_pwd) != $user_key) {
131                    return false;
132               }
133          }
134
135          $this->user_id = $rs->user_id;
136          $this->user_change_pwd = (boolean) $rs->user_change_pwd;
137          $this->user_admin = (boolean) $rs->user_super;
138
139          $this->user_info['user_pwd'] = $rs->user_pwd;
140          $this->user_info['user_name'] = $rs->user_name;
141          $this->user_info['user_firstname'] = $rs->user_firstname;
142          $this->user_info['user_displayname'] = $rs->user_displayname;
143          $this->user_info['user_email'] = $rs->user_email;
144          $this->user_info['user_url'] = $rs->user_url;
145          $this->user_info['user_default_blog'] = $rs->user_default_blog;
146          $this->user_info['user_lang'] = $rs->user_lang;
147          $this->user_info['user_tz'] = $rs->user_tz;
148          $this->user_info['user_post_status'] = $rs->user_post_status;
149          $this->user_info['user_creadt'] = $rs->user_creadt;
150          $this->user_info['user_upddt'] = $rs->user_upddt;
151
152          $this->user_info['user_cn'] = dcUtils::getUserCN($rs->user_id, $rs->user_name,
153          $rs->user_firstname, $rs->user_displayname);
154
155          $this->user_options = array_merge($this->core->userDefaults(),$rs->options());
156
157          $this->user_prefs = new dcPrefs($this->core,$this->user_id);
158
159          # Get permissions on blogs
160          if ($check_blog && ($this->findUserBlog() === false)) {
161               return false;
162          }
163          return true;
164     }
165
166     /**
167      * This method crypt given string (password, session_id, …).
168      *
169      * @param string $pwd string to be crypted
170      * @return string crypted value
171      */
172     public function crypt($pwd)
173     {
174          return crypt::hmac(DC_MASTER_KEY,$pwd,DC_CRYPT_ALGO);
175     }
176
177     /**
178     * This method only check current user password.
179     *
180     * @param string     $pwd           User password
181     * @return boolean
182     */
183     public function checkPassword($pwd)
184     {
185          if (!empty($this->user_info['user_pwd'])) {
186               return $pwd == $this->user_info['user_pwd'];
187          }
188
189          return false;
190     }
191
192     /**
193     * This method checks if user session cookie exists
194     *
195     * @return boolean
196     */
197     public function sessionExists()
198     {
199          return isset($_COOKIE[DC_SESSION_NAME]);
200     }
201
202     /**
203     * This method checks user session validity.
204     *
205     * @return boolean
206     */
207     public function checkSession($uid=null)
208     {
209          $this->core->session->start();
210
211          # If session does not exist, logout.
212          if (!isset($_SESSION['sess_user_id'])) {
213               $this->core->session->destroy();
214               return false;
215          }
216
217          # Check here for user and IP address
218          $this->checkUser($_SESSION['sess_user_id']);
219          $uid = $uid ?: http::browserUID(DC_MASTER_KEY);
220
221          $user_can_log = $this->userID() !== null && $uid == $_SESSION['sess_browser_uid'];
222
223          if (!$user_can_log) {
224               $this->core->session->destroy();
225               return false;
226          }
227
228          return true;
229     }
230
231     /**
232     * Checks if user must change his password in order to login.
233     *
234     * @return boolean
235     */
236     public function mustChangePassword()
237     {
238          return $this->user_change_pwd;
239     }
240
241     /**
242     * Checks if user is super admin
243     *
244     * @return boolean
245     */
246     public function isSuperAdmin()
247     {
248          return $this->user_admin;
249     }
250
251     /**
252     * Checks if user has permissions given in <var>$permissions</var> for blog
253     * <var>$blog_id</var>. <var>$permissions</var> is a coma separated list of
254     * permissions.
255     *
256     * @param string     $permissions   Permissions list
257     * @param string     $blog_id       Blog ID
258     * @return boolean
259     */
260     public function check($permissions,$blog_id)
261     {
262          if ($this->user_admin) {
263               return true;
264          }
265
266          $p = array_map('trim',explode(',',$permissions));
267          $b = $this->getPermissions($blog_id);
268
269          if ($b != false)
270          {
271               if (isset($b['admin'])) {
272                    return true;
273               }
274
275               foreach ($p as $v)
276               {
277                    if (isset($b[$v])) {
278                         return true;
279                    }
280               }
281          }
282
283          return false;
284     }
285
286     /**
287     * Returns true if user is allowed to change its password.
288     *
289     * @return boolean
290     */
291     public function allowPassChange()
292     {
293          return $this->allow_pass_change;
294     }
295     //@}
296
297     /// @name User code handlers
298     //@{
299     public function getUserCode()
300     {
301          $code =
302          pack('a32',$this->userID()).
303          pack('H*',$this->crypt($this->getInfo('user_pwd')));
304          return bin2hex($code);
305     }
306
307     public function checkUserCode($code)
308     {
309          $code = @pack('H*',$code);
310
311          $user_id = trim(@pack('a32',substr($code,0,32)));
312          $pwd = @unpack('H*hex',substr($code,32));
313
314          if ($user_id === false || $pwd === false) {
315               return false;
316          }
317
318          $pwd = $pwd['hex'];
319
320          $strReq = 'SELECT user_id, user_pwd '.
321                    'FROM '.$this->user_table.' '.
322                    "WHERE user_id = '".$this->con->escape($user_id)."' ";
323
324          $rs = $this->con->select($strReq);
325
326          if ($rs->isEmpty()) {
327               return false;
328          }
329
330          if ($this->crypt($rs->user_pwd) != $pwd) {
331               return false;
332          }
333
334          return $rs->user_id;
335     }
336     //@}
337
338
339     /// @name Sudo
340     //@{
341     /**
342     * Calls $f function with super admin rights.
343     * Returns the function result.
344     *
345     * @param callback   $f             Callback function
346     * @return mixed
347     */
348     public function sudo($f)
349     {
350          if (!is_callable($f)) {
351               throw new Exception($f.' function doest not exist');
352          }
353
354          $args = func_get_args();
355          array_shift($args);
356
357          if ($this->user_admin) {
358               $res = call_user_func_array($f,$args);
359          } else {
360               $this->user_admin = true;
361               try {
362                    $res = call_user_func_array($f,$args);
363                    $this->user_admin = false;
364               } catch (Exception $e) {
365                    $this->user_admin = false;
366                    throw $e;
367               }
368          }
369
370          return $res;
371     }
372     //@}
373
374     /// @name User information and options
375     //@{
376     /**
377     * Returns user permissions for a blog as an array which looks like:
378     *
379     *  - [blog_id]
380     *    - [permission] => true
381     *    - ...
382     *
383     * @param string     $blog_id       Blog ID
384     * @return array
385     */
386     public function getPermissions($blog_id)
387     {
388          if (isset($this->blogs[$blog_id])) {
389               return $this->blogs[$blog_id];
390          }
391
392          if ($this->user_admin) {
393               $strReq = 'SELECT blog_id '.
394                    'from '.$this->blog_table.' '.
395                    "WHERE blog_id = '".$this->con->escape($blog_id)."' ";
396               $rs = $this->con->select($strReq);
397
398               $this->blogs[$blog_id] = $rs->isEmpty() ? false : array('admin' => true);
399
400               return $this->blogs[$blog_id];
401          }
402
403          $strReq = 'SELECT permissions '.
404                    'FROM '.$this->perm_table.' '.
405                    "WHERE user_id = '".$this->con->escape($this->user_id)."' ".
406                    "AND blog_id = '".$this->con->escape($blog_id)."' ".
407                    "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') ";
408          $rs = $this->con->select($strReq);
409
410          $this->blogs[$blog_id] = $rs->isEmpty() ? false : $this->parsePermissions($rs->permissions);
411
412          return $this->blogs[$blog_id];
413     }
414
415    public function getBlogCount() {
416          if ($this->blog_count === null) {
417               $this->blog_count = $this->core->getBlogs(array(),true)->f(0);
418          }
419
420        return $this->blog_count;
421    }
422
423     public function findUserBlog($blog_id=null)
424     {
425          if ($blog_id && $this->getPermissions($blog_id) !== false)
426          {
427               return $blog_id;
428          }
429          else
430          {
431               if ($this->user_admin)
432               {
433                    $strReq = 'SELECT blog_id '.
434                              'FROM '.$this->blog_table.' '.
435                              'ORDER BY blog_id ASC '.
436                              $this->con->limit(1);
437               }
438               else
439               {
440                    $strReq = 'SELECT blog_id '.
441                              'FROM '.$this->perm_table.' '.
442                              "WHERE user_id = '".$this->con->escape($this->user_id)."' ".
443                              "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') ".
444                              'ORDER BY blog_id ASC '.
445                              $this->con->limit(1);
446               }
447
448               $rs = $this->con->select($strReq);
449               if (!$rs->isEmpty()) {
450                    return $rs->blog_id;
451               }
452          }
453
454          return false;
455     }
456
457     /**
458     * Returns current user ID
459     *
460     * @return string
461     */
462     public function userID()
463     {
464          return $this->user_id;
465     }
466
467     /**
468     * Returns information about a user .
469     *
470     * @param string     $n             Information name
471     * @return string
472     */
473     public function getInfo($n)
474     {
475          if (isset($this->user_info[$n])) {
476               return $this->user_info[$n];
477          }
478
479          return null;
480     }
481
482     /**
483     * Returns a specific user option
484     *
485     * @param string     $n             Option name
486     * @return string
487     */
488     public function getOption($n)
489     {
490          if (isset($this->user_options[$n])) {
491               return $this->user_options[$n];
492          }
493          return null;
494     }
495
496     /**
497     * Returns all user options in an associative array.
498     *
499     * @return array
500     */
501     public function getOptions()
502     {
503          return $this->user_options;
504     }
505     //@}
506
507     /// @name Permissions
508     //@{
509     /**
510     * Returns an array with permissions parsed from the string <var>$level</var>
511     *
512     * @param string     $level         Permissions string
513     * @return array
514     */
515     public function parsePermissions($level)
516     {
517          $level = preg_replace('/^\|/','',$level);
518          $level = preg_replace('/\|$/','',$level);
519
520          $res = array();
521          foreach (explode('|',$level) as $v) {
522               $res[$v] = true;
523          }
524          return $res;
525     }
526
527     /**
528     * Returns <var>perm_types</var> property content.
529     *
530     * @return array
531     */
532     public function getPermissionsTypes()
533     {
534          return $this->perm_types;
535     }
536
537     /**
538     * Adds a new permission type.
539     *
540     * @param string     $name          Permission name
541     * @param string     $title         Permission title
542     */
543     public function setPermissionType($name,$title)
544     {
545          $this->perm_types[$name] = $title;
546     }
547     //@}
548
549     /// @name Password recovery
550     //@{
551     /**
552     * Add a recover key to a specific user identified by its email and
553     * password.
554     *
555     * @param string     $user_id       User ID
556     * @param string     $user_email    User Email
557     * @return string
558     */
559     public function setRecoverKey($user_id,$user_email)
560     {
561          $strReq = 'SELECT user_id '.
562                    'FROM '.$this->user_table.' '.
563                    "WHERE user_id = '".$this->con->escape($user_id)."' ".
564                    "AND user_email = '".$this->con->escape($user_email)."' ";
565
566          $rs = $this->con->select($strReq);
567
568          if ($rs->isEmpty()) {
569               throw new Exception(__('That user does not exist in the database.'));
570          }
571
572          $key = md5(uniqid());
573
574          $cur = $this->con->openCursor($this->user_table);
575          $cur->user_recover_key = $key;
576
577          $cur->update("WHERE user_id = '".$this->con->escape($user_id)."'");
578
579          return $key;
580     }
581
582     /**
583     * Creates a new user password using recovery key. Returns an array:
584     *
585     * - user_email
586     * - user_id
587     * - new_pass
588     *
589     * @param string     $recover_key   Recovery key
590     * @return array
591     */
592     public function recoverUserPassword($recover_key)
593     {
594          $strReq = 'SELECT user_id, user_email '.
595                    'FROM '.$this->user_table.' '.
596                    "WHERE user_recover_key = '".$this->con->escape($recover_key)."' ";
597
598          $rs = $this->con->select($strReq);
599
600          if ($rs->isEmpty()) {
601               throw new Exception(__('That key does not exist in the database.'));
602          }
603
604          $new_pass = crypt::createPassword();
605
606          $cur = $this->con->openCursor($this->user_table);
607          $cur->user_pwd = $this->crypt($new_pass);
608          $cur->user_recover_key = null;
609
610          $cur->update("WHERE user_recover_key = '".$this->con->escape($recover_key)."'");
611
612          return array('user_email' => $rs->user_email, 'user_id' => $rs->user_id, 'new_pass' => $new_pass);
613     }
614     //@}
615
616     /** @name User management callbacks
617     This 3 functions only matter if you extend this class and use
618     DC_AUTH_CLASS constant.
619     These are called after core user management functions.
620     Could be useful if you need to add/update/remove stuff in your
621     LDAP directory or other third party authentication database.
622     */
623     //@{
624
625     /**
626     * Called after core->addUser
627     * @see dcCore::addUser
628     * @param cursor     $cur           User cursor
629     */
630     public function afterAddUser($cur) {}
631
632     /**
633     * Called after core->updUser
634     * @see dcCore::updUser
635     * @param string     $id            User ID
636     * @param cursor     $cur           User cursor
637     */
638     public function afterUpdUser($id,$cur) {}
639
640     /**
641     * Called after core->delUser
642     * @see dcCore::delUser
643     * @param string     $id            User ID
644     */
645     public function afterDelUser($id) {}
646     //@}
647}
Note: See TracBrowser for help on using the repository browser.

Sites map