Dotclear

source: inc/core/class.dc.auth.php @ 0:54703be25dd6

Revision 0:54703be25dd6, 14.5 KB checked in by Dsls <dsls@…>, 14 years ago (diff)

2.3 branch (trunk) first checkin

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

Sites map