]> CyberLeo.Net >> Repos - SourceForge/phpwiki.git/blob - lib/WikiUserNew.php
Added admin user, password user, and preference classes. Added
[SourceForge/phpwiki.git] / lib / WikiUserNew.php
1 <?php //-*-php-*-
2 rcs_id('$Id: WikiUserNew.php,v 1.2 2003-12-03 21:45:48 carstenklapp Exp $');
3
4 // This is a complete rewrite of the old WikiUser code but it is not
5 // implemented yet. Much of the existing UserPreferences class should
6 // work fine with this but a few other parts of PhpWiki need to be
7 // refitted: main.php, config.php, index.php. --Carsten
8
9
10 // Returns a user object, which contains the user's preferences.
11 //
12 // Given no name, returns an _AnonUser (anonymous user) object, who
13 // may or may not have a cookie. Given a user name, returns a
14 // _BogoUser object, who may or may not have a cookie and/or
15 // NamesakePage, a _PassUser object or an _AdminUser object.
16 //
17 // Takes care of passwords, all preference loading/storing in the
18 // user's page and any cookies. main.php will query the user object to
19 // verify the password as appropriate.
20
21
22 define('WIKIAUTH_ANON', 0);       // Not signed in.
23 define('WIKIAUTH_BOGO', 1);       // Any valid WikiWord is enough.
24 define('WIKIAUTH_USER', 2);       // Bogo user with a password.
25 define('WIKIAUTH_ADMIN', 10);     // UserName == ADMIN_USER.
26 define('WIKIAUTH_FORBIDDEN', -1); // Completely not allowed.
27
28 if (!defined('COOKIE_EXPIRATION_DAYS')) define('COOKIE_EXPIRATION_DAYS', 365);
29 if (!defined('COOKIE_DOMAIN'))          define('COOKIE_DOMAIN', '/');
30
31 if (!defined('EDITWIDTH_MIN_COLS'))     define('EDITWIDTH_MIN_COLS',     30);
32 if (!defined('EDITWIDTH_MAX_COLS'))     define('EDITWIDTH_MAX_COLS',    150);
33 if (!defined('EDITWIDTH_DEFAULT_COLS')) define('EDITWIDTH_DEFAULT_COLS', 80);
34
35 if (!defined('EDITHEIGHT_MIN_ROWS'))     define('EDITHEIGHT_MIN_ROWS',      5);
36 if (!defined('EDITHEIGHT_MAX_ROWS'))     define('EDITHEIGHT_MAX_ROWS',     80);
37 if (!defined('EDITHEIGHT_DEFAULT_ROWS')) define('EDITHEIGHT_DEFAULT_ROWS', 22);
38
39 define('TIMEOFFSET_MIN_HOURS', -26);
40 define('TIMEOFFSET_MAX_HOURS',  26);
41 if (!defined('TIMEOFFSET_DEFAULT_HOURS')) define('TIMEOFFSET_DEFAULT_HOURS', 0);
42
43
44 function WikiUser ($UserName = '') {
45 //TODO: check sessionvar for username & save username into sessionvar
46 //TODO: how to implement PassUser?
47     switch ($UserName) {
48         case (ADMIN_USER):
49             return new _AdminUser($UserName);
50             break;
51         case (true):
52             return new _BogoUser($UserName);
53             break;
54         default:
55             // check for autologin pref in cookie and upgrade user object
56             $_AnonUser = new _AnonUser();
57             if ($UserName = $_AnonUser->UserName && $_AnonUser->_prefs->get('autologin')) {
58                 if ($UserName == ADMIN_USER)
59                     return new _AdminUser($UserName);
60                 else
61                     return new _BogoUser($UserName);
62             }
63             return $_AnonUser;
64     }
65
66     // For the future... think about...
67     // if (isa($user, '_AdminUser'))
68     // if (isa($user, '_DeactivatedUser'))
69     // etc.
70 }
71
72 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
73
74 // Base _WikiUser class.
75 class _WikiUser
76 {
77     var $_level = WIKIAUTH_FORBIDDEN;
78     var $_prefs = false;
79     var _HomePagehandle = false;
80
81     var $UserName = '';
82
83     // constructor
84     function _WikiUser($UserName = '') {
85         if ($UserName) {
86             $this->UserName = $UserName;
87             $this->_HomePagehandle = $this->hasHomePage();
88         }
89         $this->loadPreferences();
90     }
91     // returns page_handle to user's home page or false if none
92     function hasHomePage() {
93         if ($this->UserName) {
94             if ($this->_HomePagehandle) {
95                 return $this->_HomePagehandle;
96             }
97             else {
98                 // check db again (maybe someone else created it since
99                 // we logged in.)
100                 global $request;
101                 $this->_HomePagehandle = $request->getPage($this->UserName);
102                 return $this->_HomePagehandle;
103             }
104         }
105         // nope
106         return false;
107     }
108
109     function checkPass($submitted_password) {
110         // By definition, an undefined user class cannot sign in.
111         return false;
112     }
113
114 }
115
116 class _AnonUser
117 extends _WikiUser
118 {
119     var $_level = WIKIAUTH_ANON;
120
121     // Anon only gets to load and save prefs in a cookie, that's it.
122     function loadPreferences() {
123         global $request;
124         if ($cookie = $request->getCookieVar(WIKI_NAME)) {
125             if (! $unboxedcookie = $this->_prefs->unpack($cookie)) {
126                 trigger_error(_("Format of UserPreferences cookie not recognised.") . " "
127                               . _("Default preferences will be used."),
128                               E_USER_WARNING);
129             }
130             // TODO: try reading userid from old PhpWiki cookie
131             // formats, then delete old cookie from browser!
132             //
133             //else {
134                 // try old cookie format.
135                 //$cookie = $request->getCookieVar('WIKI_ID');
136             //}
137
138             /**
139              * Only keep the cookie if it matches the UserName who is
140              * signing in or if this really is an Anon login (no
141              * username). (Remember, _BogoUser and higher inherit this
142              * function too!).
143              */
144             if (! $this->UserName || $this->UserName == $unboxedcookie['userid']) {
145                 $this->_prefs = new UserPreferences($unboxedcookie);
146                 $this->UserName = $unboxedcookie['userid'];
147             }
148         }
149     }
150     function savePreferences() {
151         // Allow for multiple wikis in same domain. Encode only the
152         // _prefs array of the UserPreference object. Ideally the
153         // prefs array should just be imploded into a single string or
154         // something so it is completely human readable by the end
155         // user. In that case stricter error checking will be needed
156         // when loading the cookie.
157         setcookie(WIKI_NAME, $this->_prefs->pack($this->_prefs->getAll()),
158                   COOKIE_EXPIRATION_DAYS, COOKIE_DOMAIN);
159     }
160 }
161
162 class _BogoUser
163 extends _AnonUser
164 {
165     var $_level = WIKIAUTH_BOGO;
166
167     function _BogoUser($UserName) {
168         $this->_username = $UserName;
169         $this->_prefs = $this->loadPreferences();
170     }
171
172     function loadPreferences() {
173         // Read cookie first, Bogo's homepage prefs could have been
174         // altered.
175         _AnonUser::loadPreferences();
176         // User may have deleted cookie, retrieve from his
177         // NamesakePage if there is one.
178         if ((! $this->_prefs) && $this->_HomePagehandle) {
179             if ($restored_from_page = $this->_prefs->unpack($this->_HomePagehandle->get('_prefs'))) {
180                 $this->_prefs = new UserPreferences($restored_from_page);
181             }
182         }
183     }
184     function savePreferences() {
185         _AnonUser::savePreferences();
186         // Encode only the _prefs array of the UserPreference object
187         $serialized = $this->_prefs->pack($this->_prefs->getAll());
188         $this->_HomePagehandle->set('_prefs', $serialized);
189     }
190
191     function checkPass($submitted_password) {
192         // By definition, BogoUser has an empty password.
193         return true;
194     }
195
196     function _checkPass($submitted_password, $stored_password) {
197 }
198
199 class _PassUser
200 extends _BogoUser
201 {
202     var $_level = WIKIAUTH_USER;
203
204     //TODO: if (ALLOW_USER_PASSWORDS)
205
206     function loadPreferences() {
207         //TODO:
208         //
209         // We don't necessarily have to read the cookie first. Since
210         // the user has a password, the prefs stored in the homepage
211         // cannot be arbitrarily altered by other Bogo users.
212     }
213
214     //TODO: alternatively obtain $stored_password from external auth
215     function checkPass($submitted_password) {
216         $stored_password = $this->_prefs->get('passwd');
217         return $this->_checkPass($submitted_password, $stored_password);
218     }
219
220     //TODO: remove crypt() function check from config.php:396
221     function _checkPass($submitted_password, $stored_password) {
222         if(!empty($submitted_password)) {
223             if (defined('ENCRYPTED_PASSWD') && ENCRYPTED_PASSWD) {
224                 // Verify against encrypted password.
225                 if (function_exists('crypt')) {
226                     if (crypt($submitted_password, $stored_password) == $stored_password )
227                         return true; // matches encrypted password
228                     else
229                         return false;
230                 }
231                 else {
232                     trigger_error(_("The crypt function is not available in this version of PHP.") . " "
233                                   . _("Please set ENCRYPTED_PASSWD to false in index.php and change ADMIN_PASSWD."),
234                                   E_USER_WARNING);
235                     return false;
236                 }
237             }
238             else {
239                 // Verify against cleartext password.
240                 if ($submitted_password == $stored_password)
241                     return true;
242                 else {
243                     // Check whether we forgot to enable ENCRYPTED_PASSWD
244                     if (function_exists('crypt')) {
245                         if (crypt($submitted_password, $stored_password) == $stored_password) {
246                             trigger_error(_("Please set ENCRYPTED_PASSWD to true in index.php."),
247                                           E_USER_WARNING);
248                             return true;
249                         }
250                     }
251                 }
252             }
253         }
254         return false;
255     }
256 }
257
258 class _AdminUser
259 extends _PassUser
260 {
261     var $_level = WIKIAUTH_ADMIN;
262
263     function checkPass($submitted_password) {
264         $stored_password = ADMIN_PASSWD;
265         return $this->_checkPass($submitted_password, $stored_password);
266     }
267 }
268
269 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
270
271 class _UserPreference
272 {
273     function _UserPreference ($default_value) {
274         $this->default_value = $default_value;
275     }
276
277     function sanify ($value) {
278         return (string)$value;
279     }
280
281     function update ($value) {
282     }
283 }
284
285 class _UserPreference_numeric
286 extends _UserPreference
287 {
288     function _UserPreference_numeric ($default, $minval = false,
289                                       $maxval = false) {
290         $this->_UserPreference((double)$default);
291         $this->_minval = (double)$minval;
292         $this->_maxval = (double)$maxval;
293     }
294
295     function sanify ($value) {
296         $value = (double)$value;
297         if ($this->_minval !== false && $value < $this->_minval)
298             $value = $this->_minval;
299         if ($this->_maxval !== false && $value > $this->_maxval)
300             $value = $this->_maxval;
301         return $value;
302     }
303 }
304
305 class _UserPreference_int
306 extends _UserPreference_numeric
307 {
308     function _UserPreference_int ($default, $minval = false, $maxval = false) {
309         $this->_UserPreference_numeric((int)$default, (int)$minval,
310                                        (int)$maxval);
311     }
312
313     function sanify ($value) {
314         return (int)parent::sanify((int)$value);
315     }
316 }
317
318 class _UserPreference_bool
319 extends _UserPreference
320 {
321     function _UserPreference_bool ($default = false) {
322         $this->_UserPreference((bool)$default);
323     }
324
325     function sanify ($value) {
326         if (is_array($value)) {
327             /* This allows for constructs like:
328              *
329              *   <input type="hidden" name="pref[boolPref][]" value="0" />
330              *   <input type="checkbox" name="pref[boolPref][]" value="1" />
331              *
332              * (If the checkbox is not checked, only the hidden input
333              * gets sent. If the checkbox is sent, both inputs get
334              * sent.)
335              */
336             foreach ($value as $val) {
337                 if ($val)
338                     return true;
339             }
340             return false;
341         }
342         return (bool) $value;
343     }
344 }
345
346 class _UserPreference_language
347 extends _UserPreference
348 {
349     function _UserPreference_language ($default = DEFAULT_LANGUAGE) {
350         $this->_UserPreference($default);
351     }
352
353     // FIXME: check for valid locale
354     function sanify ($value) {
355         // Revert to DEFAULT_LANGUAGE if user does not specify
356         // language in UserPreferences or chooses <system language>.
357         if ($value == '' or empty($value))
358             $value = DEFAULT_LANGUAGE;
359
360         return (string) $value;
361     }
362 }
363
364 class _UserPreference_theme
365 extends _UserPreference
366 {
367     function _UserPreference_theme ($default = THEME) {
368         $this->_UserPreference($default);
369     }
370
371     function sanify ($value) {
372         if (file_exists($this->_themefile($value)))
373             return $value;
374         return $this->default_value;
375     }
376
377     function update ($newvalue) {
378         global $Theme;
379         include_once($this->_themefile($newvalue));
380         if (empty($Theme))
381             include_once($this->_themefile(THEME));
382     }
383
384     function _themefile ($theme) {
385         return "themes/$theme/themeinfo.php";
386     }
387 }
388
389 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
390
391 // don't save default preferences for efficiency.
392 class UserPreferences
393 {
394     function UserPreferences ($saved_prefs = false) {
395         // userid stored too, to ensure the prefs are being loaded for
396         // the correct (currently signing in) userid if stored in a
397         // cookie.
398         $this->_prefs
399             = array(
400                     'userid'        => new _UserPreference(''),
401                     'passwd'        => new _UserPreference(''),
402                     'autologin'     => new _UserPreference_bool(),
403                     'email'         => new _UserPreference(''),
404                     'emailVerified' => new _UserPreference_bool(),
405                     'notifyPages'   => new _UserPreference(''),
406                     'theme'         => new _UserPreference_theme(THEME),
407                     'lang'          => new _UserPreference_language(DEFAULT_LANGUAGE),
408                     'editWidth'     => new _UserPreference_int(EDITWIDTH_DEFAULT_COLS,
409                                                                EDITWIDTH_MIN_COLS,
410                                                                EDITWIDTH_MAX_COLS),
411                     'noLinkIcons'   => new _UserPreference_bool(),
412                     'editHeight'    => new _UserPreference_int(EDITHEIGHT_DEFAULT_ROWS,
413                                                                EDITHEIGHT_MIN_ROWS,
414                                                                EDITHEIGHT_DEFAULT_ROWS),
415                     'timeOffset'    => new _UserPreference_numeric(TIMEOFFSET_DEFAULT_HOURS,
416                                                                    TIMEOFFSET_MIN_HOURS,
417                                                                    TIMEOFFSET_MAX_HOURS),
418                     'relativeDates' => new _UserPreference_bool()
419                     );
420
421         if (is_array($saved_prefs)) {
422             foreach ($saved_prefs as $name => $value)
423                 $this->set($name, $value);
424         }
425     }
426
427     function _getPref ($name) {
428         if (!isset($this->_prefs[$name])) {
429             if ($name == 'passwd2') return false;
430             trigger_error("$name: unknown preference", E_USER_NOTICE);
431             return false;
432         }
433         return $this->_prefs[$name];
434     }
435
436     function get ($name) {
437         if (isset($this->_prefs[$name]))
438             return $this->_prefs[$name];
439         if (!($pref = $this->_getPref($name)))
440             return false;
441         return $pref->default_value;
442     }
443
444     function set ($name, $value) {
445         if (!($pref = $this->_getPref($name)))
446             return false;
447
448         $newvalue = $pref->sanify($value);
449         $oldvalue = $this->get($name);
450
451         // update on changes
452         if ($newvalue != $oldvalue)
453             $pref->update($newvalue);
454
455         // don't set default values to save space (in cookies, db and
456         // sesssion)
457         if ($value == $pref->default_value)
458             unset($this->_prefs[$name]);
459         else
460             $this->_prefs[$name] = $newvalue;
461     }
462
463     function getAll() {
464         return $this->_prefs;
465     }
466
467     function pack($nonpacked) {
468         return serialize($nonpacked);
469     }
470     function unpack($packed) {
471         if (!$packed)
472             return false;
473         if (substr($packed, 0, 2) == "O:") {
474             // Looks like a serialized object
475             return unserialize($packed);
476         }
477         //trigger_error("DEBUG: Can't unpack bad UserPreferences",
478         //E_USER_WARNING);
479         return false;
480     }
481
482     function hash () {
483         return hash($this->_prefs);
484     }
485 }
486
487
488 // $Log: not supported by cvs2svn $
489 // Revision 1.1  2003/12/02 05:46:36  carstenklapp
490 // Complete rewrite of WikiUser.php.
491 //
492 // This should make it easier to hook in user permission groups etc. some
493 // time in the future. Most importantly, to finally get UserPreferences
494 // fully working properly for all classes of users: AnonUser, BogoUser,
495 // AdminUser; whether they have a NamesakePage (PersonalHomePage) or not,
496 // want a cookie or not, and to bring back optional AutoLogin with the
497 // UserName stored in a cookie--something that was lost after PhpWiki had
498 // dropped the default http auth login method.
499 //
500 // Added WikiUser classes which will (almost) work together with existing
501 // UserPreferences class. Other parts of PhpWiki need to be updated yet
502 // before this code can be hooked up.
503 //
504
505 // Local Variables:
506 // mode: php
507 // tab-width: 8
508 // c-basic-offset: 4
509 // c-hanging-comment-ender-p: nil
510 // indent-tabs-mode: nil
511 // End:
512 ?>