CWIS Developer Documentation

- Home
- Main Page
- Namespaces
- Classes
- Files

- File List
- File Members

User.php
Go to the documentation of this file.
1 <?PHP
2 #
3 # User.php
4 # An Object for Handling User Information
5 #
6 # Copyright 1999-2001 Axis Data
7 # This code is free software that can be used or redistributed under the
8 # terms of Version 2 of the GNU General Public License, as published by the
9 # Free Software Foundation (http://www.fsf.org).
10 #
11 # Author: Edward Almasy (almasy@axisdata.com)
12 #
13 # Part of the AxisPHP library v1.2.4
14 # For more information see http://www.axisdata.com/AxisPHP/
15 #
16
17 # status values (error codes)
18 define("U_OKAY", 0);
19 define("U_ERROR", 1);
20 define("U_BADPASSWORD", 2);
21 define("U_NOSUCHUSER", 3);
22 define("U_PASSWORDSDONTMATCH", 4);
23 define("U_EMAILSDONTMATCH", 5);
24 define("U_DUPLICATEUSERNAME", 6);
25 define("U_ILLEGALUSERNAME", 7);
26 define("U_EMPTYUSERNAME", 8);
27 define("U_ILLEGALPASSWORD", 9);
28 define("U_ILLEGALPASSWORDAGAIN", 10);
29 define("U_EMPTYPASSWORD", 11);
30 define("U_EMPTYPASSWORDAGAIN", 12);
31 define("U_ILLEGALEMAIL", 13);
32 define("U_ILLEGALEMAILAGAIN", 14);
33 define("U_EMPTYEMAIL", 15);
34 define("U_EMPTYEMAILAGAIN", 16);
35 define("U_NOTLOGGEDIN", 17);
36 define("U_MAILINGERROR", 18);
37 define("U_TEMPLATENOTFOUND", 19);
38 define("U_DUPLICATEEMAIL", 20);
39 define("U_NOTACTIVATED", 21);
40 define("U_PASSWORDCONTAINSUSERNAME", 22);
41 define("U_PASSWORDCONTAINSEMAIL", 23);
42 define("U_PASSWORDTOOSHORT", 24);
43 define("U_PASSWORDTOOSIMPLE", 25);
44 define("U_PASSWORDNEEDSPUNCTUATION", 26);
45 define("U_PASSWORDNEEDSMIXEDCASE", 27);
46 define("U_PASSWORDNEEDSDIGIT", 28);
47
48 class User
49 {
50 # ---- CLASS CONSTANTS ---------------------------------------------------
51 const PW_REQUIRE_PUNCTUATION = 1;
52 const PW_REQUIRE_MIXEDCASE = 2;
53 const PW_REQUIRE_DIGITS = 4;
54
55 # ---- PUBLIC INTERFACE --------------------------------------------------
56
57 public function __construct($UserInfoOne = NULL, $UserInfoTwo = NULL)
58 {
59 # create database connection
60 $this->DB = new Database();
61
62 # if we're looking up a user by UserId
63 if (is_numeric($UserInfoOne) || is_numeric($UserInfoTwo))
64 {
65 $UserId = is_numeric($UserInfoOne) ? $UserInfoOne : $UserInfoTwo;
66 $this->DB->Query("SELECT * FROM APUsers"
67 ." WHERE UserId='".intval($UserId)."'");
68 $Record = $this->DB->FetchRow();
69 }
70 # if we're looking up a user by name or email address
71 elseif (is_string($UserInfoOne) || is_string($UserInfoTwo))
72 {
73 $UserName = is_string($UserInfoOne) ? $UserInfoOne : $UserInfoTwo;
74 $this->DB->Query("SELECT * FROM APUsers"
75 ." WHERE UserName='".addslashes($UserName)."'");
76 $Record = $this->DB->FetchRow();
77
78 if ($Record === FALSE)
79 {
80 $this->DB->Query("SELECT * FROM APUsers"
81 ." WHERE EMail='".addslashes(
82 self::NormalizeEMailAddress($UserName))."'");
83 $Record = $this->DB->FetchRow();
84 }
85 }
86 # if a UserId is available from the session
87 elseif (isset($_SESSION["APUserId"]))
88 {
89 $UserId = $_SESSION["APUserId"];
90 $this->DB->Query("SELECT * FROM APUsers"
91 ." WHERE UserId='".intval($UserId)."'");
92 $Record = $this->DB->FetchRow();
93 }
94 # otherwise, create an anonymous user
95 else
96 {
97 $Record = array(
98 "UserId" => NULL,
99 "LoggedIn" => FALSE);
100 }
101
102 # if a record was found, load data from it
103 if ($Record !== FALSE)
104 {
105 $this->DBFields = $Record;
106 $this->UserId = $Record["UserId"];
107 $this->LoggedIn = $Record["LoggedIn"] ? TRUE : FALSE;
108 $this->Result = U_OKAY;
109 }
110 else
111 {
112 # otherwise, set code indicating no user found
113 $this->Result = U_NOSUCHUSER;
114 }
115 }
116
117 public function Status()
118 {
119 return $this->Result;
120 }
121
122 # return text message corresponding to current status code
123 public function StatusMessage()
124 {
125 return self::GetStatusMessageForCode($this->Result);
126 }
127
133 public static function GetStatusMessageForCode($StatusCode)
134 {
135 $APUserStatusMessages = array(
136 U_OKAY => "The operation was successful.",
137 U_ERROR => "There has been an error.",
138 U_BADPASSWORD => "The password you entered was incorrect.",
139 U_NOSUCHUSER => "No such user name was found.",
140 U_PASSWORDSDONTMATCH =>
141 "The new passwords you entered do not match.",
142 U_EMAILSDONTMATCH =>
143 "The e-mail addresses you entered do not match.",
144 U_DUPLICATEUSERNAME =>
145 "The user name you requested is already in use.",
146 U_ILLEGALUSERNAME =>
147 "The user name you requested is too short, too long, "
148 ."does not start with a letter, "
149 ."or contains illegal characters.",
150 U_ILLEGALPASSWORD =>
151 "The new password you requested is not valid.",
152 U_ILLEGALEMAIL =>
153 "The e-mail address you entered appears to be invalid.",
154 U_NOTLOGGEDIN => "The user is not logged in.",
155 U_MAILINGERROR =>
156 "An error occurred while attempting to send e-mail. "
157 ."Please notify the system administrator.",
158 U_TEMPLATENOTFOUND =>
159 "An error occurred while attempting to generate e-mail. "
160 ."Please notify the system administrator.",
161 U_DUPLICATEEMAIL =>
162 "The e-mail address you supplied already has an account "
163 ."associated with it.",
164 U_PASSWORDCONTAINSUSERNAME =>
165 "The password you entered contains your username.",
166 U_PASSWORDCONTAINSEMAIL =>
167 "The password you entered contains your email address.",
168 U_EMPTYPASSWORD => "Passwords may not be empty.",
169 U_PASSWORDTOOSHORT =>
170 "Passwords must be at least ".self::$PasswordMinLength
171 ." characters long.",
172 U_PASSWORDTOOSIMPLE =>
173 "Passwords must have at least ".self::$PasswordMinUniqueChars
174 ." different characters.",
175 U_PASSWORDNEEDSPUNCTUATION =>
176 "Passwords must contain at least one punctuation character.",
177 U_PASSWORDNEEDSMIXEDCASE =>
178 "Passwords must contain a mixture of uppercase and "
179 ."lowercase letters",
180 U_PASSWORDNEEDSDIGIT =>
181 "Passwords must contain at least one number.",
182 );
183
184 return (isset($APUserStatusMessages[$StatusCode]) ?
185 $APUserStatusMessages[$StatusCode] :
186 "Unknown user status code: ".$StatusCode );
187 }
188
189 public function Delete()
190 {
191 # clear priv list values
192 $this->DB->Query("DELETE FROM APUserPrivileges WHERE UserId = '"
193 .$this->UserId."'");
194
195 # delete user record from database
196 $this->DB->Query("DELETE FROM APUsers WHERE UserId = '".$this->UserId."'");
197
198 # report to caller that everything succeeded
199 $this->Result = U_OKAY;
200 return $this->Result;
201 }
202
208 public static function SetEmailFunction($NewValue)
209 {
210 if (is_callable($NewValue))
211 {
212 self::$EmailFunc = $NewValue;
213 }
214 }
215
216 # ---- Getting/Setting Values --------------------------------------------
217
218 public function Id()
219 {
220 return $this->UserId;
221 }
222 public function Name()
223 {
224 return $this->Get("UserName");
225 }
226
232 public function GetBestName()
233 {
234 $RealName = $this->Get("RealName");
235
236 # the real name is available, so use it
237 if (strlen(trim($RealName)))
238 {
239 return $RealName;
240 }
241
242 # the real name isn't available, so use the user name
243 return $this->Get("UserName");
244 }
245
246 public function LastLocation($NewLocation = NULL)
247 {
248 # return NULL if not associated with a particular user
249 if ($this->UserId === NULL) { return NULL; }
250
251 if ($NewLocation)
252 {
253 $this->DB->Query("UPDATE APUsers SET"
254 ." LastLocation = '".addslashes($NewLocation)."',"
255 ." LastActiveDate = NOW(),"
256 ." LastIPAddress = '".$_SERVER["REMOTE_ADDR"]."'"
257 ." WHERE UserId = '".addslashes($this->UserId)."'");
258 if (isset($this->DBFields))
259 {
260 $this->DBFields["LastLocation"] = $NewLocation;
261 $this->DBFields["LastActiveDate"] = date("Y-m-d H:i:s");
262 }
263 }
264 return $this->Get("LastLocation");
265 }
266 public function LastActiveDate()
267 {
268 return $this->Get("LastActiveDate");
269 }
270 public function LastIPAddress()
271 {
272 return $this->Get("LastIPAddress");
273 }
274
275 # get value from specified field
276 public function Get($FieldName)
277 {
278 # return NULL if not associated with a particular user
279 if ($this->UserId === NULL) { return NULL; }
280
281 return $this->UpdateValue($FieldName);
282 }
283
284 # get value (formatted as a date) from specified field
285 public function GetDate($FieldName, $Format = "")
286 {
287 # return NULL if not associated with a particular user
288 if ($this->UserId === NULL) { return NULL; }
289
290 # retrieve specified value from database
291 if (strlen($Format) > 0)
292 {
293 $this->DB->Query("SELECT DATE_FORMAT(`".addslashes($FieldName)
294 ."`, '".addslashes($Format)."') AS `".addslashes($FieldName)
295 ."` FROM APUsers WHERE UserId='".$this->UserId."'");
296 }
297 else
298 {
299 $this->DB->Query("SELECT `".addslashes($FieldName)."` FROM APUsers WHERE UserId='".$this->UserId."'");
300 }
301 $Record = $this->DB->FetchRow();
302
303 # return value to caller
304 return $Record[$FieldName];
305 }
306
307 # set value in specified field
308 public function Set($FieldName, $NewValue)
309 {
310 # return error if not associated with a particular user
311 if ($this->UserId === NULL) { return U_NOTLOGGEDIN; }
312
313 # transform booleans to 0 or 1 for storage
314 if (is_bool($NewValue))
315 {
316 $NewValue = $NewValue ? 1 : 0;
317 }
318
319
320 $this->UpdateValue($FieldName, $NewValue);
321 $this->Result = U_OKAY;
322 return $this->Result;
323 }
324
325
326 # ---- Login Functions ---------------------------------------------------
327
328 public function Login($UserName, $Password, $IgnorePassword = FALSE)
329 {
330 # if user not found in DB
331 $this->DB->Query("SELECT * FROM APUsers"
332 ." WHERE UserName = '"
333 .addslashes(self::NormalizeUserName($UserName))."'");
334 if ($this->DB->NumRowsSelected() < 1)
335 {
336 # result is no user by that name
337 $this->Result = U_NOSUCHUSER;
338 }
339 else
340 {
341 # if user account not yet activated
342 $Record = $this->DB->FetchRow();
343 if (!$Record["RegistrationConfirmed"])
344 {
345 # result is user registration not confirmed
346 $this->Result = U_NOTACTIVATED;
347 }
348 else
349 {
350 # grab password from DB
351 $StoredPassword = $Record["UserPassword"];
352
353 if (isset($Password[0]) && $Password[0] == " ")
354 {
355 $Challenge = md5(date("Ymd").$_SERVER["REMOTE_ADDR"]);
356 $StoredPassword = md5( $Challenge . $StoredPassword );
357
358 $EncryptedPassword = trim($Password);
359 }
360 else
361 {
362 # if supplied password matches encrypted password
363 $EncryptedPassword = crypt($Password, $StoredPassword);
364 }
365
366 if (($EncryptedPassword == $StoredPassword) || $IgnorePassword)
367 {
368 # result is success
369 $this->Result = U_OKAY;
370
371 # store user ID for session
372 $this->UserId = $Record["UserId"];
373 $_SESSION["APUserId"] = $this->UserId;
374
375 # update last login date
376 $this->DB->Query("UPDATE APUsers SET LastLoginDate = NOW(),"
377 ." LoggedIn = '1'"
378 ." WHERE UserId = '".$this->UserId."'");
379
380 # Check for old format hashes, and rehash if possible
381 if ($EncryptedPassword === $StoredPassword &&
382 substr($StoredPassword, 0, 3) !== "$1$" &&
383 $Password[0] !== " " &&
384 CRYPT_MD5 )
385 {
386 $NewPassword = crypt($Password, self::GetSaltForCrypt() );
387 $this->DB->Query(
388 "UPDATE APUsers SET UserPassword='"
389 .addslashes($NewPassword)."' "
390 ."WHERE UserId='".$this->UserId."'");
391 }
392
393 # since self::DBFields might already have been set to false if
394 # the user wasn't logged in when this is called, populate it
395 # with user data so that a call to self::UpdateValue will be
396 # able to properly fetch the data associated with the user
397 $this->DBFields = $Record;
398
399 # set flag to indicate we are logged in
400 $this->LoggedIn = TRUE;
401 }
402 else
403 {
404 # result is bad password
405 $this->Result = U_BADPASSWORD;
406 }
407 }
408 }
409
410 # return result to caller
411 return $this->Result;
412 }
413
414 # log this user out
415 public function Logout()
416 {
417 # clear user ID (if any) for session
418 unset($_SESSION["APUserId"]);
419
420 # if user is marked as logged in
421 if ($this->IsLoggedIn())
422 {
423 # set flag to indicate user is no longer logged in
424 $this->LoggedIn = FALSE;
425
426 # clear login flag in database
427 $this->DB->Query(
428 "UPDATE APUsers SET LoggedIn = '0' "
429 ."WHERE UserId='".$this->UserId."'");
430 }
431 }
432
433 public function GetPasswordSalt($UserName)
434 {
435 $this->DB->Query(
436 "SELECT * FROM APUsers WHERE UserName = '"
437 .addslashes(self::NormalizeUserName($UserName))."'");
438
439 if ($this->DB->NumRowsSelected() < 1)
440 {
441 # result is no user by that name, generate a fake salt
442 # to discourage user enumeration. Make it be an old-format
443 # crypt() salt so that it's harder.
444 $SaltString = $_SERVER["SERVER_ADDR"].$UserName;
445 $Result = substr(base64_encode(md5($SaltString)), 0, 2);
446 }
447 else
448 {
449 # grab password from DB
450 # Assumes that we used php's crypt() for the passowrd
451 # management stuff, and will need to be changed if we
452 # go to something else.
453 $Record = $this->DB->FetchRow();
454 $StoredPassword = $Record["UserPassword"];
455
456 if (substr($StoredPassword, 0, 3) === "$1$")
457 {
458 $Result = substr($StoredPassword, 0, 12);
459 }
460 else
461 {
462 $Result = substr($StoredPassword, 0, 2);
463 }
464 }
465
466 return $Result;
467 }
468
474 public function IsLoggedIn()
475 {
476 if (!isset($this->LoggedIn))
477 {
478 $this->LoggedIn = $this->DB->Query("
479 SELECT LoggedIn FROM APUsers
480 WHERE UserId='".addslashes($this->UserId)."'",
481 "LoggedIn") ? TRUE : FALSE;
482 }
483 return $this->LoggedIn;
484 }
485
491 public function IsNotLoggedIn()
492 {
493 return $this->IsLoggedIn() ? FALSE : TRUE;
494 }
495
500 public function IsAnonymous()
501 {
502 return ($this->UserId === NULL) ? TRUE : FALSE;
503 }
504
505
506 # ---- Password Functions ------------------------------------------------
507
508 # set new password (with checks against old password)
509
516 public function ChangePassword($OldPassword, $NewPassword, $NewPasswordAgain)
517 {
518 # return error if not associated with a particular user
519 if ($this->UserId === NULL)
520 {
521 return U_NOTLOGGEDIN;
522 }
523
524 # if old password is not correct
525 $StoredPassword = $this->DB->Query("SELECT UserPassword FROM APUsers"
526 ." WHERE UserId='".$this->UserId."'", "UserPassword");
527 $EncryptedPassword = crypt($OldPassword, $StoredPassword);
528 if ($EncryptedPassword != $StoredPassword)
529 {
530 # set status to indicate error
531 $this->Result = U_BADPASSWORD;
532 }
533 # else if both instances of new password do not match
534 elseif (self::NormalizePassword($NewPassword)
535 != self::NormalizePassword($NewPasswordAgain))
536 {
537 # set status to indicate error
538 $this->Result = U_PASSWORDSDONTMATCH;
539 }
540 # perform other validity checks
541 elseif (!self::IsValidPassword(
542 $NewPassword, $this->Get("UserName"), $this->Get("EMail")) )
543 {
544 # set status to indicate error
545 $this->Result = U_ILLEGALPASSWORD;
546 }
547 else
548 {
549 # set new password
550 $this->SetPassword($NewPassword);
551
552 # set status to indicate password successfully changed
553 $this->Result = U_OKAY;
554 }
555
556 # report to caller that everything succeeded
557 return $this->Result;
558 }
559
560 # set new password
561 public function SetPassword($NewPassword)
562 {
563 # generate encrypted password
564 $EncryptedPassword = crypt(self::NormalizePassword($NewPassword),
565 self::GetSaltForCrypt() );
566
567 # save encrypted password
568 $this->UpdateValue("UserPassword", $EncryptedPassword);
569 }
570
571 public function SetEncryptedPassword($NewEncryptedPassword)
572 {
573 # save encrypted password
574 $this->UpdateValue("UserPassword", $NewEncryptedPassword);
575 }
576
577 # get code for user to submit to confirm registration
578 public function GetActivationCode()
579 {
580 # code is MD5 sum based on user name and encrypted password
581 $ActivationCodeLength = 6;
582 return $this->GetUniqueCode("Activation", $ActivationCodeLength);
583 }
584
585 # check whether confirmation code is valid
586 public function IsActivationCodeGood($Code)
587 {
588 return (strtoupper(trim($Code)) == $this->GetActivationCode())
589 ? TRUE : FALSE;
590 }
591
592 # get/set whether user registration has been confirmed
593 public function IsActivated($NewValue = DB_NOVALUE)
594 {
595 return $this->UpdateValue("RegistrationConfirmed", $NewValue);
596 }
597
598 # get code for user to submit to confirm password reset
599 public function GetResetCode()
600 {
601 # code is MD5 sum based on user name and encrypted password
602 $ResetCodeLength = 10;
603 return $this->GetUniqueCode("Reset", $ResetCodeLength);
604 }
605
606 # check whether password reset code is valid
607 public function IsResetCodeGood($Code)
608 {
609 return (strtoupper(trim($Code)) == $this->GetResetCode())
610 ? TRUE : FALSE;
611 }
612
613 # get code for user to submit to confirm mail change request
614 public function GetMailChangeCode()
615 {
616 $ResetCodeLength = 10;
617 return $this->GetUniqueCode("MailChange".$this->Get("EMail")
618 .$this->Get("EMailNew"),
619 $ResetCodeLength);
620 }
621
622 public function IsMailChangeCodeGood($Code)
623 {
624 return (strtoupper(trim($Code)) == $this->GetMailChangeCode())
625 ? TRUE : FALSE;
626 }
627
628 # send e-mail to user (returns TRUE on success)
629 public function SendEMail(
630 $TemplateTextOrFileName, $FromAddress = NULL, $MoreSubstitutions = NULL,
631 $ToAddress = NULL)
632 {
633 # if template is file name
634 if (@is_file($TemplateTextOrFileName))
635 {
636 # load in template from file
637 $Template = file($TemplateTextOrFileName, 1);
638
639 # report error to caller if template load failed
640 if ($Template == FALSE)
641 {
642 $this->Status = U_TEMPLATENOTFOUND;
643 return $this->Status;
644 }
645
646 # join into one text block
647 $TemplateTextOrFileName = join("", $Template);
648 }
649
650 # split template into lines
651 $Template = explode("\n", $TemplateTextOrFileName);
652
653 # strip any comments out of template
654 $FilteredTemplate = array();
655 foreach ($Template as $Line)
656 {
657 if (!preg_match("/^[\\s]*#/", $Line))
658 {
659 $FilteredTemplate[] = $Line;
660 }
661 }
662
663 # split subject line out of template (first non-comment line in file)
664 $EMailSubject = array_shift($FilteredTemplate);
665 $EMailBody = join("\n", $FilteredTemplate);
666
667 # set up our substitutions
668 $Substitutions = array(
669 "X-USERNAME-X" => $this->Get("UserName"),
670 "X-EMAILADDRESS-X" => $this->Get("EMail"),
671 "X-ACTIVATIONCODE-X" => $this->GetActivationCode(),
672 "X-RESETCODE-X" => $this->GetResetCode(),
673 "X-CHANGECODE-X" => $this->GetMailChangeCode(),
674 "X-IPADDRESS-X" => @$_SERVER["REMOTE_ADDR"],
675 );
676
677 # if caller provided additional substitutions
678 if (is_array($MoreSubstitutions))
679 {
680 # add in entries from caller to substitution list
681 $Substitutions = array_merge(
682 $Substitutions, $MoreSubstitutions);
683 }
684
685 # perform substitutions on subject and body of message
686 $EMailSubject = str_replace(array_keys($Substitutions),
687 array_values($Substitutions), $EMailSubject);
688 $EMailBody = str_replace(array_keys($Substitutions),
689 array_values($Substitutions), $EMailBody);
690
691 $AdditionalHeaders = "Auto-Submitted: auto-generated";
692
693 # if caller provided "From" address
694 if ($FromAddress)
695 {
696 # prepend "From" address onto message
697 $AdditionalHeaders .= "\r\nFrom: ".$FromAddress;
698 }
699
700 # send out mail message
701 if (is_callable(self::$EmailFunc))
702 {
703 $Result = call_user_func(self::$EmailFunc,
704 is_null($ToAddress)?$this->Get("EMail"):$ToAddress,
705 $EMailSubject, $EMailBody, $AdditionalHeaders);
706 }
707 else
708 {
709 $Result = mail(is_null($ToAddress)?$this->Get("EMail"):$ToAddress,
710 $EMailSubject,
711 $EMailBody, $AdditionalHeaders);
712 }
713
714 # report result of mailing attempt to caller
715 $this->Status = ($Result == TRUE) ? U_OKAY : U_MAILINGERROR;
716 return ($this->Status == U_OKAY);
717 }
718
719
720 # ---- Privilege Functions -----------------------------------------------
721
730 public function HasPriv($Privilege, $Privileges = NULL)
731 {
732 # return FALSE if not associated with a particular user
733 if ($this->UserId === NULL) { return FALSE; }
734
735 # bail out if empty array of privileges passed in
736 if (is_array($Privilege) && !count($Privilege) && (func_num_args() < 2))
737 { return FALSE; }
738
739 # set up beginning of database query
740 $Query = "SELECT COUNT(*) AS PrivCount FROM APUserPrivileges "
741 ."WHERE UserId='".$this->UserId."' AND (";
742
743 # add first privilege(s) to query (first arg may be single value or array)
744 if (is_array($Privilege))
745 {
746 $Sep = "";
747 foreach ($Privilege as $Priv)
748 {
749 $Query .= $Sep."Privilege='".addslashes($Priv)."'";
750 $Sep = " OR ";
751 }
752 }
753 else
754 {
755 $Query .= "Privilege='".$Privilege."'";
756 $Sep = " OR ";
757 }
758
759 # add any privileges from additional args to query
760 $Args = func_get_args();
761 array_shift($Args);
762 foreach ($Args as $Arg)
763 {
764 $Query .= $Sep."Privilege='".$Arg."'";
765 $Sep = " OR ";
766 }
767
768 # close out query
769 $Query .= ")";
770
771 # look for privilege in database
772 $PrivCount = $this->DB->Query($Query, "PrivCount");
773
774 # return value to caller
775 return ($PrivCount > 0) ? TRUE : FALSE;
776 }
777
786 public static function GetSqlQueryForUsersWithPriv($Privilege, $Privileges = NULL)
787 {
788 # set up beginning of database query
789 $Query = "SELECT DISTINCT UserId FROM APUserPrivileges "
790 ."WHERE ";
791
792 # add first privilege(s) to query (first arg may be single value or array)
793 if (is_array($Privilege))
794 {
795 $Sep = "";
796 foreach ($Privilege as $Priv)
797 {
798 $Query .= $Sep."Privilege='".addslashes($Priv)."'";
799 $Sep = " OR ";
800 }
801 }
802 else
803 {
804 $Query .= "Privilege='".$Privilege."'";
805 $Sep = " OR ";
806 }
807
808 # add any privileges from additional args to query
809 $Args = func_get_args();
810 array_shift($Args);
811 foreach ($Args as $Arg)
812 {
813 $Query .= $Sep."Privilege='".$Arg."'";
814 $Sep = " OR ";
815 }
816
817 # return query to caller
818 return $Query;
819 }
820
829 public static function GetSqlQueryForUsersWithoutPriv($Privilege, $Privileges = NULL)
830 {
831 # set up beginning of database query
832 $Query = "SELECT DISTINCT UserId FROM APUserPrivileges "
833 ."WHERE ";
834
835 # add first privilege(s) to query (first arg may be single value or array)
836 if (is_array($Privilege))
837 {
838 $Sep = "";
839 foreach ($Privilege as $Priv)
840 {
841 $Query .= $Sep."Privilege != '".addslashes($Priv)."'";
842 $Sep = " AND ";
843 }
844 }
845 else
846 {
847 $Query .= "Privilege != '".$Privilege."'";
848 $Sep = " AND ";
849 }
850
851 # add any privileges from additional args to query
852 $Args = func_get_args();
853 array_shift($Args);
854 foreach ($Args as $Arg)
855 {
856 $Query .= $Sep."Privilege != '".$Arg."'";
857 $Sep = " AND ";
858 }
859
860 # return query to caller
861 return $Query;
862 }
863
864 public function GrantPriv($Privilege)
865 {
866 # return error if not associated with a particular user
867 if ($this->UserId === NULL) { return U_NOTLOGGEDIN; }
868
869 # if privilege value is invalid
870 if (intval($Privilege) != trim($Privilege))
871 {
872 # set code to indicate error
873 $this->Result = U_ERROR;
874 }
875 else
876 {
877 # if user does not already have privilege
878 $PrivCount = $this->DB->Query("SELECT COUNT(*) AS PrivCount"
879 ." FROM APUserPrivileges"
880 ." WHERE UserId='".$this->UserId."'"
881 ." AND Privilege='".$Privilege."'",
882 "PrivCount");
883 if ($PrivCount == 0)
884 {
885 # add privilege for this user to database
886 $this->DB->Query("INSERT INTO APUserPrivileges"
887 ." (UserId, Privilege) VALUES"
888 ." ('".$this->UserId."', ".$Privilege.")");
889 }
890
891 # set code to indicate success
892 $this->Result = U_OKAY;
893 }
894
895 # report result to caller
896 return $this->Result;
897 }
898
899 public function RevokePriv($Privilege)
900 {
901 # return error if not associated with a particular user
902 if ($this->UserId === NULL) { return U_NOTLOGGEDIN; }
903
904 # remove privilege from database (if present)
905 $this->DB->Query("DELETE FROM APUserPrivileges"
906 ." WHERE UserId = '".$this->UserId."'"
907 ." AND Privilege = '".$Privilege."'");
908
909 # report success to caller
910 $this->Result = U_OKAY;
911 return $this->Result;
912 }
913
914 public function GetPrivList()
915 {
916 # return empty list if not associated with a particular user
917 if ($this->UserId === NULL) { return array(); }
918
919 # read privileges from database and return array to caller
920 $this->DB->Query("SELECT Privilege FROM APUserPrivileges"
921 ." WHERE UserId='".$this->UserId."'");
922 return $this->DB->FetchColumn("Privilege");
923 }
924
925 public function SetPrivList($NewPrivileges)
926 {
927 # return error if not associated with a particular user
928 if ($this->UserId === NULL) { return U_NOTLOGGEDIN; }
929
930 # clear old priv list values
931 $this->DB->Query("DELETE FROM APUserPrivileges"
932 ." WHERE UserId='".$this->UserId."'");
933
934 # for each priv value passed in
935 foreach ($NewPrivileges as $Privilege)
936 {
937 # set priv for user
938 $this->GrantPriv($Privilege);
939 }
940 }
941
948 public static function GetAnonymousUser()
949 {
950 # if we have a UserId in the session, move it aside
951 if (isset($_SESSION["APUserId"]))
952 {
953 $OldUserId = $_SESSION["APUserId"];
954 unset($_SESSION["APUserId"]);
955 }
956
957 # create a new anonymous user
958 $CalledClass = get_called_class();
959
960 $Result = new $CalledClass();
961
962 # restore the $_SESSION value
963 if (isset($OldUserId))
964 {
965 $_SESSION["APUserId"] = $OldUserId;
966 }
967
968 # return our anonymous user
969 return $Result;
970 }
971
972 # ---- Miscellaneous Functions -------------------------------------------
973
974 # get unique alphanumeric code for user
975 public function GetUniqueCode($SeedString, $CodeLength)
976 {
977 # return NULL if not associated with a particular user
978 if ($this->UserId === NULL) { return NULL; }
979
980 return substr(strtoupper(md5(
981 $this->Get("UserName").$this->Get("UserPassword").$SeedString)),
982 0, $CodeLength);
983 }
984
985
986 # ---- PRIVATE INTERFACE -------------------------------------------------
987
988 protected $DB; # handle to SQL database we use to store user information
989 protected $UserId = NULL; # user ID number for reference into database
990 protected $Result; # result of last operation
991 protected $LoggedIn; # flag indicating whether user is logged in
992 protected $Status;
993
994 private $DBFields; # used for caching user values
995
996 # optional mail function to use instead of mail()
997 private static $EmailFunc = NULL;
998
999 # check whether a user name is valid
1000 # (alphanumeric string of 2-24 chars that starts with a letter)
1001 public static function IsValidUserName($UserName)
1002 {
1003 if (preg_match("/^[a-zA-Z][a-zA-Z0-9]{1,23}$/", $UserName))
1004 {
1005 return TRUE;
1006 }
1007 return FALSE;
1008 }
1009
1010 # check whether a password is valid (at least 6 characters)
1011 public static function IsValidPassword(
1012 $Password, $UserName, $Email)
1013 {
1014 return count(self::CheckPasswordForErrors(
1015 $Password, $UserName, $Email)) == 0 ?
1016 TRUE : FALSE ;
1017 }
1018
1028 public static function CheckPasswordForErrors(
1029 $Password, $UserName = NULL, $Email = NULL)
1030 {
1031 # start off assuming no errors
1032 $Errors = array();
1033
1034 # normalize incoming password
1035 $Password = self::NormalizePassword($Password);
1036
1037 # username provided and password contains username
1038 if ($UserName !== NULL &&
1039 stripos($Password, $UserName) !== FALSE)
1040 {
1041 $Errors[]= U_PASSWORDCONTAINSUSERNAME;
1042 }
1043
1044 # email provided and password contains email
1045 if ($Email !== NULL &&
1046 stripos($Password, $Email) !== FALSE)
1047 {
1048 $Errors[]= U_PASSWORDCONTAINSEMAIL;
1049 }
1050
1051 # length requirement
1052 if (strlen($Password) == 0)
1053 {
1054 $Errors[]= U_EMPTYPASSWORD;
1055 }
1056 elseif (strlen($Password) < self::$PasswordMinLength)
1057 {
1058 $Errors[]= U_PASSWORDTOOSHORT;
1059 }
1060
1061 # unique characters requirement
1062 $UniqueChars = count(array_unique(
1063 preg_split('//u', $Password, NULL, PREG_SPLIT_NO_EMPTY)));
1064
1065 if ($UniqueChars < self::$PasswordMinUniqueChars)
1066 {
1067 $Errors[]= U_PASSWORDTOOSIMPLE;
1068 }
1069
1070 # for the following complexity checks, use unicode character properties
1071 # in PCRE as in: http://php.net/manual/en/regexp.reference.unicode.php
1072
1073 # check for punctuation, uppercase letters, and numbers as per the system
1074 # configuration
1075 if (self::$PasswordRules & self::PW_REQUIRE_PUNCTUATION &&
1076 !preg_match('/\p{P}/u', $Password) )
1077 {
1078 $Errors[]= U_PASSWORDNEEDSPUNCTUATION;
1079 }
1080
1081 if (self::$PasswordRules & self::PW_REQUIRE_MIXEDCASE &&
1082 (!preg_match('/\p{Lu}/u', $Password) ||
1083 !preg_match('/\p{Ll}/u', $Password) ) )
1084
1085 {
1086 $Errors[]= U_PASSWORDNEEDSMIXEDCASE;
1087 }
1088
1089 if (self::$PasswordRules & self::PW_REQUIRE_DIGITS &&
1090 !preg_match('/\p{N}/u', $Password))
1091 {
1092 $Errors[]= U_PASSWORDNEEDSDIGIT;
1093 }
1094
1095 return $Errors;
1096 }
1097
1098 # check whether an e-mail address looks valid
1099 public static function IsValidLookingEMailAddress($EMail)
1100 {
1101 if (preg_match("/^[a-zA-Z0-9._\-]+@[a-zA-Z0-9._\-]+\.[a-zA-Z]{2,3}$/",
1102 $EMail))
1103 {
1104 return TRUE;
1105 }
1106 else
1107 {
1108 return FALSE;
1109 }
1110 }
1111
1112 # get normalized version of e-mail address
1113 public static function NormalizeEMailAddress($EMailAddress)
1114 {
1115 return strtolower(trim($EMailAddress));
1116 }
1117
1118 # get normalized version of user name
1119 public static function NormalizeUserName($UserName)
1120 {
1121 return trim($UserName);
1122 }
1123
1124 # get normalized version of password
1125 public static function NormalizePassword($Password)
1126 {
1127 return trim($Password);
1128 }
1129
1130 # generate random password
1131 public function GetRandomPassword($PasswordMinLength = 6, $PasswordMaxLength = 8)
1132 {
1133 # seed random number generator
1134 mt_srand((double)microtime() * 1000000);
1135
1136 # generate password of requested length
1137 return sprintf("%06d", mt_rand(pow(10, ($PasswordMinLength - 1)),
1138 (pow(10, $PasswordMaxLength) - 1)));
1139 }
1140
1141 # convenience function to supply parameters to Database->UpdateValue()
1142 public function UpdateValue($FieldName, $NewValue = DB_NOVALUE)
1143 {
1144 return $this->DB->UpdateValue("APUsers", $FieldName, $NewValue,
1145 "UserId = '".$this->UserId."'", $this->DBFields);
1146 }
1147
1148 # methods for backward compatibility with earlier versions of User
1149 public function GivePriv($Privilege)
1150 {
1151 $this->GrantPriv($Privilege);
1152 }
1153
1159 public static function SetPasswordRules($NewValue)
1160 {
1161 self::$PasswordRules = $NewValue;
1162 }
1163
1168 public static function SetPasswordMinLength($NewValue)
1169 {
1170 self::$PasswordMinLength = $NewValue;
1171 }
1172
1177 public static function SetPasswordMinUniqueChars($NewValue)
1178 {
1179 self::$PasswordMinUniqueChars = $NewValue;
1180 }
1181
1186 public static function GetPasswordRulesDescription()
1187 {
1188 return "Passwords are case-sensitive, cannot contain your username or email, "
1189 ."must be at least ".self::$PasswordMinLength
1190 ." characters long, "
1191 ." have at least ".self::$PasswordMinUniqueChars
1192 ." different characters"
1193 .(self::$PasswordRules & self::PW_REQUIRE_PUNCTUATION ?
1194 ", include punctuation":"")
1195 .(self::$PasswordRules & self::PW_REQUIRE_MIXEDCASE ?
1196 ", include capital and lowercase letters":"")
1197 .(self::$PasswordRules & self::PW_REQUIRE_DIGITS ?
1198 ", include a number":"").".";
1199 }
1200
1204 private static function GetSaltForCrypt()
1205 {
1206 # generate a password salt by grabbing CRYPT_SALT_LENGTH
1207 # random bytes, then base64 encoding while filtering out
1208 # non-alphanumeric characters to get a string all the hashes
1209 # accept as a salt
1210 $Salt = preg_replace("/[^A-Za-z0-9]/","",
1211 base64_encode(openssl_random_pseudo_bytes(
1212 CRYPT_SALT_LENGTH) ));
1213
1214 # select the best available hashing algorithm, provide a salt
1215 # in the correct format for that algorithm
1216 if (CRYPT_SHA512==1)
1217 {
1218 return '$6$'.substr($Salt, 0, 16);
1219 }
1220 elseif (CRYPT_SHA256==1)
1221 {
1222 return '$5$'.substr($Salt, 0, 16);
1223 }
1224 elseif (CRYPT_BLOWFISH==1)
1225 {
1226 return '$2y$'.substr($Salt, 0, 22);
1227 }
1228 elseif (CRYPT_MD5==1)
1229 {
1230 return '$1$'.substr($Salt, 0, 12);
1231 }
1232 elseif (CRYPT_EXT_DES==1)
1233 {
1234 return '_'.substr($Salt, 0, 8);
1235 }
1236 else
1237 {
1238 return substr($Salt, 0, 2);
1239 }
1240 }
1241
1242 private static $PasswordMinLength = 6;
1243 private static $PasswordMinUniqueChars = 4;
1244
1245 # default to no additional requirements beyond length
1246 private static $PasswordRules = 0;
1247 }
User\Get
Get($FieldName)
Definition: User.php:276
User\$DB
$DB
Definition: User.php:988
User\GetRandomPassword
GetRandomPassword($PasswordMinLength=6, $PasswordMaxLength=8)
Definition: User.php:1131
User\GetAnonymousUser
static GetAnonymousUser()
Get the anonymous user (i.e., the User object that exists when no user is logged in), useful when a permission check needs to know if something should be visible to the general public.
Definition: User.php:948
User\NormalizeUserName
static NormalizeUserName($UserName)
Definition: User.php:1119
U_TEMPLATENOTFOUND
const U_TEMPLATENOTFOUND
Definition: User.php:37
User\IsLoggedIn
IsLoggedIn()
Report whether user is currently logged in.
Definition: User.php:474
User\IsValidLookingEMailAddress
static IsValidLookingEMailAddress($EMail)
Definition: User.php:1099
User\SetPasswordMinLength
static SetPasswordMinLength($NewValue)
Set password minimum length.
Definition: User.php:1168
User\GetMailChangeCode
GetMailChangeCode()
Definition: User.php:614
User\GetUniqueCode
GetUniqueCode($SeedString, $CodeLength)
Definition: User.php:975
User\GivePriv
GivePriv($Privilege)
Definition: User.php:1149
User\__construct
__construct($UserInfoOne=NULL, $UserInfoTwo=NULL)
Definition: User.php:57
User\GrantPriv
GrantPriv($Privilege)
Definition: User.php:864
Database
SQL database abstraction object with smart query caching.
Definition: Database.php:22
User\CheckPasswordForErrors
static CheckPasswordForErrors($Password, $UserName=NULL, $Email=NULL)
Determine if a provided password complies with the configured rules, optionally checking that it does...
Definition: User.php:1028
User\IsMailChangeCodeGood
IsMailChangeCodeGood($Code)
Definition: User.php:622
U_EMAILSDONTMATCH
const U_EMAILSDONTMATCH
Definition: User.php:23
User\GetResetCode
GetResetCode()
Definition: User.php:599
User\UpdateValue
UpdateValue($FieldName, $NewValue=DB_NOVALUE)
Definition: User.php:1142
User\NormalizePassword
static NormalizePassword($Password)
Definition: User.php:1125
U_EMPTYPASSWORD
const U_EMPTYPASSWORD
Definition: User.php:29
U_PASSWORDNEEDSPUNCTUATION
const U_PASSWORDNEEDSPUNCTUATION
Definition: User.php:44
User\GetActivationCode
GetActivationCode()
Definition: User.php:578
U_ERROR
const U_ERROR
Definition: User.php:19
U_PASSWORDNEEDSDIGIT
const U_PASSWORDNEEDSDIGIT
Definition: User.php:46
U_NOTLOGGEDIN
const U_NOTLOGGEDIN
Definition: User.php:35
U_BADPASSWORD
const U_BADPASSWORD
Definition: User.php:20
U_ILLEGALEMAIL
const U_ILLEGALEMAIL
Definition: User.php:31
User\Login
Login($UserName, $Password, $IgnorePassword=FALSE)
Definition: User.php:328
User
Definition: User.php:48
User\SetEncryptedPassword
SetEncryptedPassword($NewEncryptedPassword)
Definition: User.php:571
U_PASSWORDCONTAINSEMAIL
const U_PASSWORDCONTAINSEMAIL
Definition: User.php:41
User\Delete
Delete()
Definition: User.php:189
User\IsResetCodeGood
IsResetCodeGood($Code)
Definition: User.php:607
User\PW_REQUIRE_MIXEDCASE
const PW_REQUIRE_MIXEDCASE
Definition: User.php:52
User\IsValidUserName
static IsValidUserName($UserName)
Definition: User.php:1001
User\GetSqlQueryForUsersWithPriv
static GetSqlQueryForUsersWithPriv($Privilege, $Privileges=NULL)
Get an SQL query that will return IDs of all users that have the specified privilege flags...
Definition: User.php:786
User\GetPasswordSalt
GetPasswordSalt($UserName)
Definition: User.php:433
User\LastLocation
LastLocation($NewLocation=NULL)
Definition: User.php:246
User\SetPasswordMinUniqueChars
static SetPasswordMinUniqueChars($NewValue)
Set password minimum unique characters.
Definition: User.php:1177
User\GetPrivList
GetPrivList()
Definition: User.php:914
User\SetPasswordRules
static SetPasswordRules($NewValue)
Set password requirements.
Definition: User.php:1159
User\IsActivated
IsActivated($NewValue=DB_NOVALUE)
Definition: User.php:593
User\GetStatusMessageForCode
static GetStatusMessageForCode($StatusCode)
Get text error message for a specified error code.
Definition: User.php:133
User\PW_REQUIRE_PUNCTUATION
const PW_REQUIRE_PUNCTUATION
Definition: User.php:51
User\HasPriv
HasPriv($Privilege, $Privileges=NULL)
Check whether user has specified privilege(s).
Definition: User.php:730
U_PASSWORDNEEDSMIXEDCASE
const U_PASSWORDNEEDSMIXEDCASE
Definition: User.php:45
User\$LoggedIn
$LoggedIn
Definition: User.php:991
User\GetBestName
GetBestName()
Get the best available name associated with a user, i.e., the real name or, if it isn&#39;t available...
Definition: User.php:232
User\SendEMail
SendEMail($TemplateTextOrFileName, $FromAddress=NULL, $MoreSubstitutions=NULL, $ToAddress=NULL)
Definition: User.php:629
PHP
PHP
Definition: RSSClient--Test.php:12
U_PASSWORDCONTAINSUSERNAME
const U_PASSWORDCONTAINSUSERNAME
Definition: User.php:40
U_PASSWORDTOOSIMPLE
const U_PASSWORDTOOSIMPLE
Definition: User.php:43
User\$Status
$Status
Definition: User.php:992
User\Set
Set($FieldName, $NewValue)
Definition: User.php:308
DB_NOVALUE
const DB_NOVALUE
Definition: Database.php:1784
U_MAILINGERROR
const U_MAILINGERROR
Definition: User.php:36
User\IsAnonymous
IsAnonymous()
Report whether user is anonymous user.
Definition: User.php:500
User\GetPasswordRulesDescription
static GetPasswordRulesDescription()
Get a string describing the password rules.
Definition: User.php:1186
U_DUPLICATEUSERNAME
const U_DUPLICATEUSERNAME
Definition: User.php:24
User\SetEmailFunction
static SetEmailFunction($NewValue)
Set email function to use instead of mail().
Definition: User.php:208
U_OKAY
const U_OKAY
Definition: User.php:18
User\IsValidPassword
static IsValidPassword($Password, $UserName, $Email)
Definition: User.php:1011
User\NormalizeEMailAddress
static NormalizeEMailAddress($EMailAddress)
Definition: User.php:1113
User\GetDate
GetDate($FieldName, $Format="")
Definition: User.php:285
U_DUPLICATEEMAIL
const U_DUPLICATEEMAIL
Definition: User.php:38
User\Status
Status()
Definition: User.php:117
User\IsNotLoggedIn
IsNotLoggedIn()
Report whether user is not currently logged in.
Definition: User.php:491
User\Logout
Logout()
Definition: User.php:415
User\LastActiveDate
LastActiveDate()
Definition: User.php:266
User\$Result
$Result
Definition: User.php:990
User\LastIPAddress
LastIPAddress()
Definition: User.php:270
U_NOSUCHUSER
const U_NOSUCHUSER
Definition: User.php:21
User\Id
Id()
Definition: User.php:218
User\SetPassword
SetPassword($NewPassword)
Definition: User.php:561
U_NOTACTIVATED
const U_NOTACTIVATED
Definition: User.php:39
User\$UserId
$UserId
Definition: User.php:989
User\SetPrivList
SetPrivList($NewPrivileges)
Definition: User.php:925
User\ChangePassword
ChangePassword($OldPassword, $NewPassword, $NewPasswordAgain)
Check provided password and set a new one if it war correct.
Definition: User.php:516
User\IsActivationCodeGood
IsActivationCodeGood($Code)
Definition: User.php:586
U_ILLEGALUSERNAME
const U_ILLEGALUSERNAME
Definition: User.php:25
User\RevokePriv
RevokePriv($Privilege)
Definition: User.php:899
U_PASSWORDTOOSHORT
const U_PASSWORDTOOSHORT
Definition: User.php:42
User\Name
Name()
Definition: User.php:222
User\PW_REQUIRE_DIGITS
const PW_REQUIRE_DIGITS
Definition: User.php:53
U_ILLEGALPASSWORD
const U_ILLEGALPASSWORD
Definition: User.php:27
User\GetSqlQueryForUsersWithoutPriv
static GetSqlQueryForUsersWithoutPriv($Privilege, $Privileges=NULL)
Get an SQL query that will return IDs of all users that do not have the specified privilege flags...
Definition: User.php:829
User\StatusMessage
StatusMessage()
Definition: User.php:123
U_PASSWORDSDONTMATCH
const U_PASSWORDSDONTMATCH
Definition: User.php:22