Dotclear

source: inc/core/class.dc.auth.php @ 2566:9bf417837888

Revision 2566:9bf417837888, 14.6 KB checked in by franck <carnet.franck.paul@…>, 12 years ago (diff)

Add some people in CREDITS, remove trailing spaces and tabs.

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 (crypt::hmac(DC_MASTER_KEY,$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 only check current user password.
168     *
169     * @param string     $pwd           User password
170     * @return boolean
171     */
172     public function checkPassword($pwd)
173     {
174          if (!empty($this->user_info['user_pwd'])) {
175               return $pwd == $this->user_info['user_pwd'];
176          }
177
178          return false;
179     }
180
181     /**
182     * This method checks if user session cookie exists
183     *
184     * @return boolean
185     */
186     public function sessionExists()
187     {
188          return isset($_COOKIE[DC_SESSION_NAME]);
189     }
190
191     /**
192     * This method checks user session validity.
193     *
194     * @return boolean
195     */
196     public function checkSession($uid=null)
197     {
198          $this->core->session->start();
199
200          # If session does not exist, logout.
201          if (!isset($_SESSION['sess_user_id'])) {
202               $this->core->session->destroy();
203               return false;
204          }
205
206          # Check here for user and IP address
207          $this->checkUser($_SESSION['sess_user_id']);
208          $uid = $uid ? $uid : http::browserUID(DC_MASTER_KEY);
209
210          $user_can_log = $this->userID() !== null && $uid == $_SESSION['sess_browser_uid'];
211
212          if (!$user_can_log) {
213               $this->core->session->destroy();
214               return false;
215          }
216
217          return true;
218     }
219
220     /**
221     * Checks if user must change his password in order to login.
222     *
223     * @return boolean
224     */
225     public function mustChangePassword()
226     {
227          return $this->user_change_pwd;
228     }
229
230     /**
231     * Checks if user is super admin
232     *
233     * @return boolean
234     */
235     public function isSuperAdmin()
236     {
237          return $this->user_admin;
238     }
239
240     /**
241     * Checks if user has permissions given in <var>$permissions</var> for blog
242     * <var>$blog_id</var>. <var>$permissions</var> is a coma separated list of
243     * permissions.
244     *
245     * @param string     $permissions   Permissions list
246     * @param string     $blog_id       Blog ID
247     * @return boolean
248     */
249     public function check($permissions,$blog_id)
250     {
251          if ($this->user_admin) {
252               return true;
253          }
254
255          $p = explode(',',$permissions);
256          $b = $this->getPermissions($blog_id);
257
258          if ($b != false)
259          {
260               if (isset($b['admin'])) {
261                    return true;
262               }
263
264               foreach ($p as $v)
265               {
266                    if (isset($b[$v])) {
267                         return true;
268                    }
269               }
270          }
271
272          return false;
273     }
274
275     /**
276     * Returns true if user is allowed to change its password.
277     *
278     * @return boolean
279     */
280     public function allowPassChange()
281     {
282          return $this->allow_pass_change;
283     }
284     //@}
285
286     /// @name User code handlers
287     //@{
288     public function getUserCode()
289     {
290          $code =
291          pack('a32',$this->userID()).
292          pack('H*',crypt::hmac(DC_MASTER_KEY,$this->getInfo('user_pwd')));
293          return bin2hex($code);
294     }
295
296     public function checkUserCode($code)
297     {
298          $code = @pack('H*',$code);
299
300          $user_id = trim(@pack('a32',substr($code,0,32)));
301          $pwd = @unpack('H40hex',substr($code,32,40));
302
303          if ($user_id === false || $pwd === false) {
304               return false;
305          }
306
307          $pwd = $pwd['hex'];
308
309          $strReq = 'SELECT user_id, user_pwd '.
310                    'FROM '.$this->user_table.' '.
311                    "WHERE user_id = '".$this->con->escape($user_id)."' ";
312
313          $rs = $this->con->select($strReq);
314
315          if ($rs->isEmpty()) {
316               return false;
317          }
318
319          if (crypt::hmac(DC_MASTER_KEY,$rs->user_pwd) != $pwd) {
320               return false;
321          }
322
323          return $rs->user_id;
324     }
325     //@}
326
327
328     /// @name Sudo
329     //@{
330     /**
331     * Calls $f function with super admin rights.
332     * Returns the function result.
333     *
334     * @param callback   $f             Callback function
335     * @return mixed
336     */
337     public function sudo($f)
338     {
339          if (!is_callable($f)) {
340               throw new Exception($f.' function doest not exist');
341          }
342
343          $args = func_get_args();
344          array_shift($args);
345
346          if ($this->user_admin) {
347               $res = call_user_func_array($f,$args);
348          } else {
349               $this->user_admin = true;
350               try {
351                    $res = call_user_func_array($f,$args);
352                    $this->user_admin = false;
353               } catch (Exception $e) {
354                    $this->user_admin = false;
355                    throw $e;
356               }
357          }
358
359          return $res;
360     }
361     //@}
362
363     /// @name User information and options
364     //@{
365     /**
366     * Returns user permissions for a blog as an array which looks like:
367     *
368     *  - [blog_id]
369     *    - [permission] => true
370     *    - ...
371     *
372     * @param string     $blog_id       Blog ID
373     * @return array
374     */
375     public function getPermissions($blog_id)
376     {
377          if (isset($this->blogs[$blog_id])) {
378               return $this->blogs[$blog_id];
379          }
380
381          if ($this->user_admin) {
382               $strReq = 'SELECT blog_id '.
383                    'from '.$this->blog_table.' '.
384                    "WHERE blog_id = '".$this->con->escape($blog_id)."' ";
385               $rs = $this->con->select($strReq);
386
387               $this->blogs[$blog_id] = $rs->isEmpty() ? false : array('admin' => true);
388
389               return $this->blogs[$blog_id];
390          }
391
392          $strReq = 'SELECT permissions '.
393                    'FROM '.$this->perm_table.' '.
394                    "WHERE user_id = '".$this->con->escape($this->user_id)."' ".
395                    "AND blog_id = '".$this->con->escape($blog_id)."' ".
396                    "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') ";
397          $rs = $this->con->select($strReq);
398
399          $this->blogs[$blog_id] = $rs->isEmpty() ? false : $this->parsePermissions($rs->permissions);
400
401          return $this->blogs[$blog_id];
402     }
403
404    public function getBlogCount() {
405          if ($this->blog_count === null) {
406               $this->blog_count = $this->core->getBlogs(array(),true)->f(0);
407          }
408
409        return $this->blog_count;
410    }
411
412     public function findUserBlog($blog_id=null)
413     {
414          if ($blog_id && $this->getPermissions($blog_id) !== false)
415          {
416               return $blog_id;
417          }
418          else
419          {
420               if ($this->user_admin)
421               {
422                    $strReq = 'SELECT blog_id '.
423                              'FROM '.$this->blog_table.' '.
424                              'ORDER BY blog_id ASC '.
425                              $this->con->limit(1);
426               }
427               else
428               {
429                    $strReq = 'SELECT blog_id '.
430                              'FROM '.$this->perm_table.' '.
431                              "WHERE user_id = '".$this->con->escape($this->user_id)."' ".
432                              "AND (permissions LIKE '%|usage|%' OR permissions LIKE '%|admin|%' OR permissions LIKE '%|contentadmin|%') ".
433                              'ORDER BY blog_id ASC '.
434                              $this->con->limit(1);
435               }
436
437               $rs = $this->con->select($strReq);
438               if (!$rs->isEmpty()) {
439                    return $rs->blog_id;
440               }
441          }
442
443          return false;
444     }
445
446     /**
447     * Returns current user ID
448     *
449     * @return string
450     */
451     public function userID()
452     {
453          return $this->user_id;
454     }
455
456     /**
457     * Returns information about a user .
458     *
459     * @param string     $n             Information name
460     * @return string
461     */
462     public function getInfo($n)
463     {
464          if (isset($this->user_info[$n])) {
465               return $this->user_info[$n];
466          }
467
468          return null;
469     }
470
471     /**
472     * Returns a specific user option
473     *
474     * @param string     $n             Option name
475     * @return string
476     */
477     public function getOption($n)
478     {
479          if (isset($this->user_options[$n])) {
480               return $this->user_options[$n];
481          }
482          return null;
483     }
484
485     /**
486     * Returns all user options in an associative array.
487     *
488     * @return array
489     */
490     public function getOptions()
491     {
492          return $this->user_options;
493     }
494     //@}
495
496     /// @name Permissions
497     //@{
498     /**
499     * Returns an array with permissions parsed from the string <var>$level</var>
500     *
501     * @param string     $level         Permissions string
502     * @return array
503     */
504     public function parsePermissions($level)
505     {
506          $level = preg_replace('/^\|/','',$level);
507          $level = preg_replace('/\|$/','',$level);
508
509          $res = array();
510          foreach (explode('|',$level) as $v) {
511               $res[$v] = true;
512          }
513          return $res;
514     }
515
516     /**
517     * Returns <var>perm_types</var> property content.
518     *
519     * @return array
520     */
521     public function getPermissionsTypes()
522     {
523          return $this->perm_types;
524     }
525
526     /**
527     * Adds a new permission type.
528     *
529     * @param string     $name          Permission name
530     * @param string     $title         Permission title
531     */
532     public function setPermissionType($name,$title)
533     {
534          $this->perm_types[$name] = $title;
535     }
536     //@}
537
538     /// @name Password recovery
539     //@{
540     /**
541     * Add a recover key to a specific user identified by its email and
542     * password.
543     *
544     * @param string     $user_id       User ID
545     * @param string     $user_email    User Email
546     * @return string
547     */
548     public function setRecoverKey($user_id,$user_email)
549     {
550          $strReq = 'SELECT user_id '.
551                    'FROM '.$this->user_table.' '.
552                    "WHERE user_id = '".$this->con->escape($user_id)."' ".
553                    "AND user_email = '".$this->con->escape($user_email)."' ";
554
555          $rs = $this->con->select($strReq);
556
557          if ($rs->isEmpty()) {
558               throw new Exception(__('That user does not exist in the database.'));
559          }
560
561          $key = md5(uniqid());
562
563          $cur = $this->con->openCursor($this->user_table);
564          $cur->user_recover_key = $key;
565
566          $cur->update("WHERE user_id = '".$this->con->escape($user_id)."'");
567
568          return $key;
569     }
570
571     /**
572     * Creates a new user password using recovery key. Returns an array:
573     *
574     * - user_email
575     * - user_id
576     * - new_pass
577     *
578     * @param string     $recover_key   Recovery key
579     * @return array
580     */
581     public function recoverUserPassword($recover_key)
582     {
583          $strReq = 'SELECT user_id, user_email '.
584                    'FROM '.$this->user_table.' '.
585                    "WHERE user_recover_key = '".$this->con->escape($recover_key)."' ";
586
587          $rs = $this->con->select($strReq);
588
589          if ($rs->isEmpty()) {
590               throw new Exception(__('That key does not exist in the database.'));
591          }
592
593          $new_pass = crypt::createPassword();
594
595          $cur = $this->con->openCursor($this->user_table);
596          $cur->user_pwd = crypt::hmac(DC_MASTER_KEY,$new_pass);
597          $cur->user_recover_key = null;
598
599          $cur->update("WHERE user_recover_key = '".$this->con->escape($recover_key)."'");
600
601          return array('user_email' => $rs->user_email, 'user_id' => $rs->user_id, 'new_pass' => $new_pass);
602     }
603     //@}
604
605     /** @name User management callbacks
606     This 3 functions only matter if you extend this class and use
607     DC_AUTH_CLASS constant.
608     These are called after core user management functions.
609     Could be useful if you need to add/update/remove stuff in your
610     LDAP directory or other third party authentication database.
611     */
612     //@{
613
614     /**
615     * Called after core->addUser
616     * @see dcCore::addUser
617     * @param cursor     $cur           User cursor
618     */
619     public function afterAddUser($cur) {}
620
621     /**
622     * Called after core->updUser
623     * @see dcCore::updUser
624     * @param string     $id            User ID
625     * @param cursor     $cur           User cursor
626     */
627     public function afterUpdUser($id,$cur) {}
628
629     /**
630     * Called after core->delUser
631     * @see dcCore::delUser
632     * @param string     $id            User ID
633     */
634     public function afterDelUser($id) {}
635     //@}
636}
Note: See TracBrowser for help on using the repository browser.

Sites map