Dotclear

source: inc/core/class.dc.auth.php @ 312:8816f31232a9

Revision 312:8816f31232a9, 14.7 KB checked in by Dsls <dsls@…>, 14 years ago (diff)

Blog association is now optional when whecking a user in dcAuth. Closes #1091

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

Sites map