Dotclear

source: inc/core/class.dc.auth.php @ 852:969647a6c35e

Revision 852:969647a6c35e, 14.5 KB checked in by Dsls <dsls@…>, 13 years ago (diff)

sexy step 3 : no more media.

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

Sites map