Dotclear

source: inc/core/class.dc.auth.php @ 3:cf375f1e7b0f

Revision 3:cf375f1e7b0f, 14.6 KB checked in by Dsls <dsls@…>, 14 years ago (diff)

Ported Franck & Kozlika updates for user prefs, dedicated branch

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

Sites map