Advertisement
Omnikron13

PHP User class 1st draft

May 30th, 2011
178
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 29.66 KB | None | 0 0
  1. <?php
  2. /*
  3. Copyright (C) 2011 by Joey Sabey (GameFreak7744@gmail.com)
  4.  
  5. Permission is hereby granted, free of charge, to any person obtaining a copy
  6. of this software and associated documentation files (the "Software"), to deal
  7. in the Software without restriction, including without limitation the rights
  8. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. copies of the Software, and to permit persons to whom the Software is
  10. furnished to do so, subject to the following conditions:
  11.  
  12. The above copyright notice and this permission notice shall be included in
  13. all copies or substantial portions of the Software.
  14.  
  15. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  21. THE SOFTWARE.
  22. */
  23.  
  24. class User
  25. {
  26.     //Configuration parameters
  27.     const DB_PATH = '/f5/gamefreakslab/public/projects/UserClass/User_example.db';
  28.     const SALT_LENGTH = 16;
  29.     const SESSION_KEY_LENGTH = 32;
  30.     const CONFIRM_CODE_LENGTH = 16;
  31.     const HASH_ALGORITHM = 'sha512';
  32.     const HASH_ITERATIONS = 256;
  33.     const USERNAME_REGEX = '/^\w{4,32}$/';
  34.     const PASSWORD_REGEX = '/^.{6,128}$/';
  35.     const EMAIL_REGEX = '/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i';
  36.     const COOKIE_SESSION_LENGTH = 604800; //In seconds = 7 days
  37.     const COOKIE_PATH = '';     //Passed to the setcookie() path parameter
  38.     const COOKIE_DOMAIN = '';   //Passed to the setcookie() domain parameter
  39.    
  40.     //Login templates
  41.     const LOGIN_FORM_TEMPLATE   = '<form id="login_form" action="[form_action]" method="POST" accept-charset="UTF-8" name="login_form">[error]<fieldset id="login_form_group"><legend id="form_legend">User login form</legend><label id="username_label" for="username_field">Username:<input id="username_field" type="text" name="username" value="[username]" /></label><label id="password_label" for="password_field">Password:<input id="password_field" type="password" name="password" /></label><label id="login_button_label" for="login_button"><input id="login_button" type="submit" value="Login" /></label></fieldset></form>';
  42.     const LOGIN_SUCCESS_TEMPLATE = '<p>Successfully logged in as [username]!</p>';
  43.    
  44.     //Register templates
  45.     const REGISTER_FORM_TEMPLATE    = '<form id="register_form" action="[form_action]" method="POST" accept-charset="UTF-8" name="register_form">[error]<fieldset id="register_form_group"><legend id="form_legend">User registration form</legend><label id="username_label" for="username_field">Username:<input id="username_field" type="text" name="username" value="[username]" /></label><label id="email_label" for="email_field">Email:<input id="email_field" type="email" name="email" value="[email]" /></label><label id="password_label" for="password_field">Password:<input id="password_field" type="password" name="password" /></label><label id="confirm_password_label" for="confirm_password_field">Confirm password:<input id="confirm_password_field" type="password" name="passwordConfirm" /></label><label id="register_button_label" for="register_button"><input id="register_button" type="submit" value="Register" /></label></fieldset></form>';
  46.     const REGISTER_SUCCESS_TEMPLATE = '<p>Your account has been successfully registered, and an email has been sent to you containing a link to confirm your email address and activate your account.</p>';
  47.    
  48.     //Login errors
  49.     const LOGIN_NO_USERNAME_ERROR = 'You must enter your username to log in';
  50.     const LOGIN_NO_PASSWORD_ERROR = 'You must enter your password to log in';
  51.     const LOGIN_NO_INPUT_ERROR = 'You must enter your username and password to log in';
  52.     const LOGIN_INVALID_USERNAME_ERROR = 'The username entered was not a valid username';
  53.     const LOGIN_INVALID_PASSWORD_ERROR = 'The password entered was not a valid password';
  54.     const LOGIN_NO_SUCH_USERNAME_ERROR = 'The username entered does not exist';
  55.     const LOGIN_INCORRECT_PASSWORD_ERROR = 'Incorrect password entered';
  56.    
  57.     //Register errors
  58.     const REGISTER_NO_USERNAME_ERROR = 'You must choose a username to register';
  59.     const REGISTER_NO_PASSWORD_ERROR = 'You must choose a password to register';
  60.     const REGISTER_NO_CONFIRM_PASSWORD_ERROR = 'You must confirm your password to register';
  61.     const REGISTER_NO_EMAIL_ERROR = 'You must enter your email address to register';
  62.     const REGISTER_INVALID_USERNAME_ERROR = 'The username you have chosen is not valid';
  63.     const REGISTER_INVALID_PASSWORD_ERROR = 'The password you have chosen is not valid';
  64.     const REGISTER_INVALID_EMAIL_ERROR = 'You must enter a valid email address to register';
  65.     const REGISTER_PASSWORD_MISMATCH_ERROR = 'The passwords you entered do not match';
  66.     const REGISTER_UNAVAILABLE_USERNAME_ERROR = 'The username you have chosen is already registered';
  67.     const REGISTER_UNAVAILABLE_EMAIL_ERROR = 'The email address you have entered is already in use at this site, you may have already registered an account';
  68.    
  69.     //Confirm templates
  70.     const CONFIRM_SUBJECT = 'Confirm your account at XYZ';
  71.     const CONFIRM_BODY_TEMPLATE = 'http://lab.s4t4n.net/projects/UserClass/demo/confirm.php?id=[id]&code=[code]';
  72.     const CONFIRM_FROM = 'accounts@lab.s4t4n.net';
  73.    
  74.     //Set email confirm templates
  75.     const SET_EMAIL_CONFIRM_SUBJECT = 'Confirm your new email address at XYZ';
  76.     const SET_EMAIL_CONFIRM_BODY_TEMPLATE = 'http://lab.s4t4n.net/projects/UserClass/demo/confirm_email.php?id=[id]&code=[code]';
  77.     const SET_EMAIL_CONFIRM_FROM = 'accounts@lab.s4t4n.net';
  78.    
  79.     //Flags
  80.     const GET_BY_ID = 0;
  81.     const GET_BY_USERNAME = 1;
  82.     const SET_EMAIL_CONFIRM = 0;
  83.     const SET_EMAIL_DIRECT = 1;
  84.    
  85.     //Class variables
  86.     protected $id = NULL;
  87.     protected $username = NULL;
  88.     protected $password = NULL;
  89.     protected $salt = NULL;
  90.     protected $email = NULL;
  91.     protected $date = NULL;
  92.     protected $sessionKey = NULL;
  93.     protected $sessionIP = NULL;
  94.    
  95.     //Class constructor; loads User data from the database by id or username
  96.     //Maybe make consturctor private/protected, limiting construction to get()?
  97.     public function __construct($uid, $getType = User::GET_BY_ID)
  98.     {
  99.         $db = new PDO('sqlite:'.User::DB_PATH);
  100.         if($getType == User::GET_BY_ID)
  101.         {
  102.             //Need to revise this exception..?
  103.             if(!is_int($uid))
  104.                 throw new InvalidArgumentException('User class constructor expected integer, value given was: '.$uid);
  105.             $query = $db->prepare('SELECT * FROM users WHERE id = :id');
  106.             $query->bindParam(':id', $uid, PDO::PARAM_INT);
  107.         }
  108.         else if($getType == User::GET_BY_USERNAME)
  109.         {
  110.             if(!User::validateUsername($uid))
  111.                 throw new UserInvalidUsernameException($uid);
  112.             $query = $db->prepare('SELECT * FROM users WHERE username = :username');
  113.             $query->bindParam(':username', $uid, PDO::PARAM_STR);
  114.         }
  115.         //else THROW ERROR
  116.         $query->execute();
  117.         $query->bindColumn('id', $this->id, PDO::PARAM_INT);
  118.         $query->bindColumn('username', $this->username, PDO::PARAM_STR);
  119.         $query->bindColumn('password', $this->password, PDO::PARAM_STR);
  120.         $query->bindColumn('salt', $this->salt, PDO::PARAM_LOB);
  121.         $query->bindColumn('email', $this->email, PDO::PARAM_STR);
  122.         $query->bindColumn('date', $this->date, PDO::PARAM_INT);
  123.         $query->bindColumn('sessionKey', $this->sessionKey, PDO::PARAM_STR);
  124.         $query->bindColumn('sessionIP', $this->sessionIP, PDO::PARAM_STR);
  125.         $query->fetch(PDO::FETCH_BOUND);
  126.         //May need to revise type of exception thrown here...
  127.         if($this->id == NULL)
  128.             throw new OutOfBoundsException('No such user found in database: '.$id);
  129.     }
  130.    
  131.     public function getID(){
  132.         return $this->id;
  133.     }
  134.     public function getUsername(){
  135.         return $this->username;
  136.     }
  137.     public function getPassword(){
  138.         return $this->password;
  139.     }
  140.     public function getSalt(){
  141.         return $this->salt;
  142.     }
  143.     public function getEmail(){
  144.         return $this->email;
  145.     }
  146.     public function getDate(){
  147.         return $this->date;
  148.     }
  149.     public function getSessionKey(){
  150.         return $this->sessionKey;
  151.     }
  152.     public function getSessionIP(){
  153.         return $this->sessionIP;
  154.     }
  155.    
  156.     //Validates $username, then updates the database & member
  157.     public function setUsername($username)
  158.     {
  159.         if(!User::validateUsername($username))
  160.             throw new UserInvalidUsernameException($username);
  161.         if(!User::availableUsername($username))
  162.             throw new UserUnavailableUsernameException($username);
  163.         $db = new PDO('sqlite:'.User::DB_PATH);
  164.         $query = $db->prepare('UPDATE users SET username=:username WHERE id=:id');
  165.         $query->bindParam(':username', $username, PDO::PARAM_STR);
  166.         $query->bindParam(':id', $this->id, PDO::PARAM_INT);
  167.         $query->execute();
  168.         $this->username = $username;
  169.     }
  170.    
  171.     //Validates $password, then updates database & member
  172.     public function setPassword($password)
  173.     {
  174.         if(!User::validatePassword($password))
  175.             throw new UserInvalidPasswordException($password);
  176.         $salt = User::generateSalt();
  177.         $password = processPassword($password, $salt);
  178.         $db = new PDO('sqlite:'.User::DB_PATH);
  179.         $query = $db->prepare('UPDATE users SET password=:password, salt=:salt WHERE id=:id');
  180.         $query->bindParam(':password', $password, PDO::PARAM_STR);
  181.         $query->bindParam(':salt', $salt, PDO::PARAM_LOB);
  182.         $query->bindParam(':id', $this->id, PDO::PARAM_INT);
  183.         $query->execute();
  184.         $this->password = $password;
  185.         $this->salt = $salt;
  186.     }
  187.    
  188.     //This method needs revision to confirm new email
  189.     public function setEmail($email, $mode = User::SET_EMAIL_CONFIRM)
  190.     {
  191.         if(!User::validateEmail($email))
  192.             throw new UserInvalidEmailException($email);
  193.         if(!User::availableEmail($email))
  194.             throw new UserUnavailableEmailException($email);
  195.         $db = new PDO('sqlite:'.User::DB_PATH);
  196.         if($mode == User::SET_EMAIL_CONFIRM)
  197.         {
  198.             $confirmCode = User::generateConfirmCode();
  199.             $query = $db->prepare('INSERT INTO usersChangeEmail(userID, email, confirmCode) VALUES(:userID, :email, :confirmCode)');
  200.             $query->bindParam(':userID', $this->id, PDO::PARAM_INT);
  201.             $query->bindParam(':email', $email, PDO::PARAM_STR);
  202.             $query->bindParam(':confirmCode', hash(User::HASH_ALGORITHM, $confirmCode), PDO::PARAM_STR);
  203.             $query->execute();
  204.             //SEND EMAIL HERE!
  205.             $body = User::SET_EMAIL_CONFIRM_BODY_TEMPLATE;
  206.             $body = str_replace('[id]', $db->lastInsertId(), $body);
  207.             $body = str_replace('[code]', $confirmCode, $body);
  208.             mail($email, User::SET_EMAIL_CONFIRM_SUBJECT, $body, 'From: '.User::SET_EMAIL_CONFIRM_FROM);
  209.         }
  210.         else if($mode == User::SET_EMAIL_DIRECT)
  211.         {
  212.             $query = $db->prepare('UPDATE users SET email=:email WHERE id=:id');
  213.             $query->bindParam(':email', $email, PDO::PARAM_STR);
  214.             $query->bindParam(':id', $this->id, PDO::PARAM_INT);
  215.             $query->execute();
  216.             $this->email = $email;
  217.         }
  218.         else
  219.             throw new InvalidArgumentException('Invalid mode for setEmail method, mode is either SET_EMAIL_CONFIRM or SET_EMAIL_DIRECT');
  220.     }
  221.    
  222.     //Checks $password against the stored password; returns true if it matches, false otherwise
  223.     public function checkPassword($password)
  224.     {
  225.         if(User::processPassword($password, $this->salt) == $this->password)
  226.             return true;
  227.         return false;
  228.     }
  229.    
  230.     //Generates a new session key; sends out login cookies; updates the database & members
  231.     public function startSession()
  232.     {
  233.         //Ready session data...
  234.         $sessionKey = User::generateSessionKey();
  235.         $hashedKey = hash(User::HASH_ALGORITHM, $sessionKey);
  236.         $sessionIP = $_SERVER['REMOTE_ADDR'];
  237.         //Send session cookies...
  238.         User::sendCookies($this->username, $sessionKey);
  239.         //Update database...
  240.         $db = new PDO('sqlite:'.User::DB_PATH);
  241.         $query = $db->prepare('UPDATE users SET sessionKey=:sessionKey, sessionIP=:sessionIP WHERE id=:id');
  242.         $query->bindParam(':sessionKey', $hashedKey, PDO::PARAM_STR);
  243.         $query->bindParam(':sessionIP', $sessionIP, PDO::PARAM_STR);
  244.         $query->bindParam(':id', $this->id, PDO::PARAM_INT);
  245.         $query->execute();
  246.         //Update members...
  247.         $this->sessionKey = $hashedKey;
  248.         $this->sessionIP = $sessionIP;
  249.     }
  250.    
  251.     //Checks if User has valid login session for the current script; checks if logged in
  252.     public function checkSession($sessionKey)
  253.     {
  254.         if($_SERVER['REMOTE_ADDR'] != $this->sessionIP)
  255.             return false;
  256.         if(hash(User::HASH_ALGORITHM, $sessionKey) != $this->sessionKey)
  257.             return false;
  258.         return true;
  259.     }
  260.    
  261.     public function endSession()
  262.     {
  263.         //Remove cookies...
  264.         User::removeCookies();
  265.         //Remove database data...
  266.         $db = new PDO('sqlite:'.User::DB_PATH);
  267.         $query = $db->prepare('UPDATE users SET sessionKey=NULL, sessionIP=NULL WHERE id=:id');
  268.         $query->bindParam(':id', $this->id, PDO::PARAM_INT);
  269.         $query->execute();
  270.         //Remove member data...
  271.         $this->sessionKey = NULL;
  272.         $this->sessionIP = NULL;
  273.     }
  274.    
  275.     //Returns a new User object representing the user currently logged in, determined by cookies
  276.     public static function getCurrent()
  277.     {
  278.         //Error checking here; check cookies are even set; throw exceptions?
  279.         $current = new User($_COOKIE['username'], User::GET_BY_USERNAME);
  280.         if($current->checkSession($_COOKIE['sessionKey']))
  281.             return $current;
  282.         return NULL;
  283.     }
  284.        
  285.     //Adds a new user straight to the database; does not require email validation!
  286.     public static function add($username, $password, $email)
  287.     {
  288.         //Error checking/validation...
  289.         if(!User::validateUsername($username))
  290.             throw new UserInvalidUsernameException($username);
  291.         if(!User::validatePassword($password))
  292.             throw new UserInvalidPasswordException($password);
  293.         if(!User::validateEmail($email))
  294.             throw new UserInvalidEmailException($email);
  295.         if(!User::availableUsername($username))
  296.             throw new UserUnavailableUsernameException($username);                                                    
  297.         if(!User::availableEmail($email))
  298.             throw new UserUnavailableEmailException($email);  
  299.         //Main code follows...
  300.         $salt = User::generateSalt();
  301.         $db = new PDO('sqlite:'.User::DB_PATH);
  302.         $query = $db->prepare('INSERT INTO users(username, password, salt, email, date) VALUES(:username, :password, :salt, :email, :date)');
  303.         $query->bindParam(':username', $username, PDO::PARAM_STR);
  304.         $query->bindParam(':password', User::processPassword($password, $salt), PDO::PARAM_STR);
  305.         $query->bindParam(':salt', $salt, PDO::PARAM_LOB); //is LOB right..?
  306.         $query->bindParam(':email', $email, PDO::PARAM_STR);
  307.         $query->bindParam(':date', time(), PDO::PARAM_STR);
  308.         $query->execute(); 
  309.     }
  310.    
  311.     //Adds a new user to the usersPending database; sends an email out for confirmation
  312.     public static function addPending($username, $password, $email)
  313.     {
  314.         //Error checking/validation...
  315.         if(!User::validateUsername($username))
  316.             throw new UserInvalidUsernameException($username);
  317.         if(!User::validatePassword($password))
  318.             throw new UserInvalidPasswordException($password);
  319.         if(!User::validateEmail($email))
  320.             throw new UserInvalidEmailException($email);
  321.         if(!User::availableUsername($username))
  322.             throw new UserUnavailableUsernameException($username);                                                    
  323.         if(!User::availableEmail($email))
  324.             throw new UserUnavailableEmailException($email);  
  325.         //Main code follows...
  326.         $salt = User::generateSalt();
  327.         $confirmCode = User::generateConfirmCode();
  328.         $db = new PDO('sqlite:'.User::DB_PATH);
  329.         $query = $db->prepare('INSERT INTO usersPending(username, password, salt, email, date, confirmCode) VALUES(:username, :password, :salt, :email, :date, :confirmCode)');
  330.         $query->bindParam(':username', $username, PDO::PARAM_STR);
  331.         $query->bindParam(':password', User::processPassword($password, $salt), PDO::PARAM_STR);
  332.         $query->bindParam(':salt', $salt, PDO::PARAM_LOB); //is LOB right..?
  333.         $query->bindParam(':email', $email, PDO::PARAM_STR);
  334.         $query->bindParam(':date', time(), PDO::PARAM_STR);
  335.         $query->bindParam(':confirmCode', hash(User::HASH_ALGORITHM, $confirmCode), PDO::PARAM_STR);
  336.         $query->execute();
  337.         //Send confirm email...
  338.         $body = User::CONFIRM_BODY_TEMPLATE;
  339.         $body = str_replace('[id]', $db->lastInsertId(), $body);
  340.         $body = str_replace('[code]', $confirmCode, $body);
  341.         mail($email, User::CONFIRM_SUBJECT, $body, 'From: '.User::CONFIRM_FROM);
  342.     }
  343.    
  344.     //Should this be a single success+act-or-error method similar to login()?
  345.     public static function confirm()
  346.     {
  347.         //validate input here..?
  348.         $db = new PDO('sqlite:'.User::DB_PATH);
  349.         $query = $db->prepare('SELECT * FROM usersPending WHERE id = :id');
  350.         $query->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
  351.         $query->execute();
  352.         $query->bindColumn('username', $username, PDO::PARAM_STR);
  353.         $query->bindColumn('password', $password, PDO::PARAM_STR);
  354.         $query->bindColumn('salt', $salt, PDO::PARAM_LOB);
  355.         $query->bindColumn('email', $email, PDO::PARAM_STR);
  356.         $query->bindColumn('date', $date, PDO::PARAM_INT);
  357.         $query->bindColumn('confirmCode', $confirmCode, PDO::PARAM_STR);
  358.         $query->fetch(PDO::FETCH_BOUND);
  359.         if($username == NULL)
  360.             return 'Could not find that account to confirm; it may already have been confirmed.'; //replace with template
  361.         if(hash(User::HASH_ALGORITHM, $_GET['code']) == $confirmCode)
  362.         {
  363.             //Copy over data to users table...
  364.             $db = new PDO('sqlite:'.User::DB_PATH);
  365.             $query = $db->prepare('INSERT INTO users(username, password, salt, email, date) VALUES(:username, :password, :salt, :email, :date)');
  366.             $query->bindParam(':username', $username, PDO::PARAM_STR);
  367.             $query->bindParam(':password', $password, PDO::PARAM_STR);
  368.             $query->bindParam(':salt', $salt, PDO::PARAM_LOB); //is LOB right..?
  369.             $query->bindParam(':email', $email, PDO::PARAM_STR);
  370.             $query->bindParam(':date', $date, PDO::PARAM_STR);
  371.             $query->execute();
  372.             //Remove entry from usersPending...
  373.             $query = $db->prepare('DELETE FROM usersPending WHERE id = :id');
  374.             $query->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
  375.             $query->execute();
  376.             return 'Email confirmed; you may now log in.';
  377.         }
  378.         return 'Confirmation code incorrect, carefully recopy the link into your browser and try again.';
  379.     }
  380.    
  381.     //This method should be called on a page setup to confirm email changes; returns success or error message
  382.     public static function confirmSetEmail()
  383.     {
  384.         //validate input here..?
  385.         $db = new PDO('sqlite:'.User::DB_PATH);
  386.         $query = $db->prepare('SELECT * FROM usersChangeEmail WHERE id = :id');
  387.         $query->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
  388.         $query->execute();
  389.         $query->bindColumn('userID', $userID, PDO::PARAM_INT);
  390.         $query->bindColumn('email', $email, PDO::PARAM_STR);
  391.         $query->bindColumn('confirmCode', $confirmCode, PDO::PARAM_STR);
  392.         $query->fetch(PDO::FETCH_BOUND);
  393.         if($email == NULL)
  394.             return 'Could not find that email change request to confirm; it may already have been confirmed.'; //TEMPLATE!
  395.         if(hash(User::HASH_ALGORITHM, $_GET['code']) == $confirmCode)
  396.         {
  397.             //Update users email in database...
  398.             $query = $db->prepare('UPDATE users SET email=:email WHERE id=:id');
  399.             $query->bindParam(':email', $email, PDO::PARAM_STR);
  400.             $query->bindParam(':id', $userID, PDO::PARAM_INT);
  401.             $query->execute();
  402.             //Remove entry from usersChangeEmail...
  403.             $query = $db->prepare('DELETE FROM usersChangeEmail WHERE id = :id');
  404.             $query->bindParam(':id', $_GET['id'], PDO::PARAM_INT);
  405.             $query->execute();
  406.             return 'Email change confirmed.';//TEMPLATE
  407.         }
  408.         return 'Confirmation code incorrect, carefully recopy the link into your browser and try again.';//TEMPLATE
  409.     }
  410.    
  411.     //This function should be called at the -top- of a login page, before any output; it returns
  412.     // either a success message (and logins in the user), or a form (with appropriate errors)
  413.     public static function login()
  414.     {
  415.         $username = NULL;
  416.         $password = NULL;
  417.         $error = NULL;
  418.        
  419.         if(isset($_POST['username']))
  420.         {
  421.             //Check if form was filled out completely...
  422.             if($_POST['username'] == '' && $_POST['password'] == '')
  423.                 return User::processLoginForm(User::LOGIN_NO_INPUT_ERROR);
  424.             if($_POST['username'] == '')
  425.                 return User::processLoginForm(User::LOGIN_NO_USERNAME_ERROR);
  426.             if($_POST['password'] == '')
  427.                 return User::processLoginForm(User::LOGIN_NO_PASSWORD_ERROR, $_POST['username']);
  428.             //Check if entered details are valid...
  429.             if(!User::validateUsername($_POST['username']))
  430.                 return User::processLoginForm(User::LOGIN_INVALID_USERNAME_ERROR);
  431.             if(!User::validatePassword($_POST['password']))
  432.                 return User::processLoginForm(User::LOGIN_INVALID_PASSWORD_ERROR, $_POST['username']);
  433.             //Try finding in the user...
  434.             $user = new User($_POST['username'], User::GET_BY_USERNAME);
  435.             if($user == NULL)
  436.                 return User::processLoginForm(User::LOGIN_NO_SUCH_USER_ERROR);
  437.             //Check if the passwords match...
  438.             if(!$user->checkPassword($_POST['password']))
  439.                 return User::processLoginForm(User::LOGIN_INCORRECT_PASSWORD_ERROR, $_POST['username']);
  440.             //Success...
  441.             $user->startSession();
  442.             return str_replace('[username]', $user->getUsername(), User::LOGIN_SUCCESS_TEMPLATE);
  443.         }
  444.         return User::processLoginForm();
  445.     }
  446.    
  447.     //This function inserts the dynamic elements into the login form template
  448.     protected static function processLoginForm($error = '', $username = '')
  449.     {
  450.         $form = User::LOGIN_FORM_TEMPLATE;
  451.         $form = str_replace('[form_action]', $_SERVER['PHP_SELF'], $form);
  452.         $form = str_replace('[error]', $error, $form);
  453.         $form = str_replace('[username]', $username, $form);
  454.         return $form;
  455.     }
  456.    
  457.     //This method should be called at the appropriate point on the registration page to
  458.     // print the form/success message; returns a string containing the form (with errors
  459.     // as necessary) or a success message
  460.     public static function register()
  461.     {
  462.         //If form hasn't been posted, return form...
  463.         if(!isset($_POST['username']))
  464.             return User::processRegisterForm();
  465.         //Check if form was filled out completely...
  466.         if($_POST['username'] == '')
  467.             return User::processRegisterForm(User::REGISTER_NO_USERNAME_ERROR, NULL, $_POST['email']);
  468.         if($_POST['email'] == '')
  469.             return User::processRegisterForm(User::REGISTER_NO_EMAIL_ERROR, $_POST['username']);
  470.         if($_POST['password'] == '')
  471.             return User::processRegisterForm(User::REGISTER_NO_PASSWORD_ERROR, $_POST['username'], $_POST['email']);
  472.         if($_POST['passwordConfirm'] == '')
  473.             return User::processRegisterForm(User::REGISTER_NO_CONFIRM_PASSWORD_ERROR, $_POST['username'], $_POST['email']);
  474.         //Check if entered details are valid...
  475.         if(!User::validateUsername($_POST['username']))
  476.             return User::processRegisterForm(User::REGISTER_INVALID_USERNAME_ERROR, NULL, $_POST['email']);
  477.         if(!User::validateEmail($_POST['email']))
  478.             return User::processRegisterForm(User::REGISTER_INVALID_EMAIL_ERROR, $_POST['username']);
  479.         if(!User::validatePassword($_POST['password']))
  480.             return User::processRegisterForm(User::REGISTER_INVALID_PASSWORD_ERROR, $_POST['username'], $_POST['email']);
  481.         //Check if username & email are available...
  482.         if(!User::availableUsername($_POST['username']))
  483.             return User::processRegisterForm(User::REGISTER_UNAVAILABLE_USERNAME_ERROR, NULL, $_POST['email']);
  484.         if(!User::availableEmail($_POST['email']))
  485.             return User::processRegisterForm(User::REGISTER_UNAVAILABLE_EMAIL_ERROR, $_POST['username']);
  486.         //Ensure passwords match...
  487.         if($_POST['password'] != $_POST['passwordConfirm'])
  488.             return User::processRegisterForm(User::REGISTER_PASSWORD_MISMATCH_ERROR, $_POST['username'], $_POST['email']);
  489.         //Add user to the usersPending table..
  490.         User::addPending($_POST['username'], $_POST['password'], $_POST['email']);
  491.         return User::REGISTER_SUCCESS_TEMPLATE;
  492.     }
  493.    
  494.     //This function inserts the dynamic elements into the register form template
  495.     protected static function processRegisterForm($error = '', $username = '', $email = '')
  496.     {
  497.         $form = User::REGISTER_FORM_TEMPLATE;
  498.         $form = str_replace('[form_action]', $_SERVER['PHP_SELF'], $form);
  499.         $form = str_replace('[error]', $error, $form);
  500.         $form = str_replace('[username]', $username, $form);
  501.         $form = str_replace('[email]', $email, $form);
  502.         return $form;
  503.     }
  504.    
  505.     //Checks that $username follows the pre-defined conventions
  506.     protected static function validateUsername($username)
  507.     {
  508.         if(preg_match(User::USERNAME_REGEX, $username))
  509.             return true;
  510.         return false;
  511.     }
  512.    
  513.     //Checkt that $password follws the pre-defined conventions
  514.     protected static function validatePassword($password)
  515.     {
  516.         if(preg_match(User::PASSWORD_REGEX, $password))
  517.             return true;
  518.         return false;
  519.     }
  520.    
  521.     //Ensures $emails at least -looks- like a real email address
  522.     protected static function validateEmail($email)
  523.     {
  524.         if(preg_match(User::EMAIL_REGEX, $email))
  525.             return true;
  526.         return false;
  527.     }
  528.    
  529.     //Checks if $username already exists in database; returns true if it doesn't, otherwise false
  530.     protected static function availableUsername($username)
  531.     {
  532.         $db = new PDO('sqlite:'.User::DB_PATH);
  533.         $query = $db->prepare('SELECT COUNT (*) FROM users WHERE username = :username');
  534.         $query->bindParam(':username', $username, PDO::PARAM_STR);
  535.         $query->execute();
  536.         if($query->fetchColumn() == 0)
  537.         {
  538.             $query = $db->prepare('SELECT COUNT (*) FROM usersPending WHERE username = :username');
  539.             $query->bindParam(':username', $username, PDO::PARAM_STR);
  540.             $query->execute();
  541.             if($query->fetchColumn() == 0)
  542.                 return true;
  543.         }
  544.         return false;
  545.     }
  546.    
  547.     //Checks if $email already exists in database; returns true if it doesn't, otherwise false
  548.     protected static function availableEmail($email)
  549.     {
  550.         $db = new PDO('sqlite:'.User::DB_PATH);
  551.         $query = $db->prepare('SELECT COUNT (*) FROM users WHERE email = :email');
  552.         $query->bindParam(':email', $email, PDO::PARAM_STR);
  553.         $query->execute();
  554.         if($query->fetchColumn() == 0)
  555.         {
  556.             $query = $db->prepare('SELECT COUNT (*) FROM usersPending WHERE email = :email');
  557.             $query->bindParam(':email', $email, PDO::PARAM_STR);
  558.             $query->execute();
  559.             if($query->fetchColumn() == 0)
  560.                 return true;
  561.         }
  562.         return false;
  563.     }
  564.    
  565.     //This method salts the password, and then hashes it multiple times
  566.     protected static function processPassword($password, $salt)
  567.     {
  568.         $salted = $password.$salt;
  569.         for($x = 0; $x < User::HASH_ITERATIONS; $x++)
  570.             $salted = hash(User::HASH_ALGORITHM, $salted);
  571.         return $salted;
  572.     }
  573.    
  574.     //Generates a random salt with a pre-determined length
  575.     protected static function generateSalt()
  576.     {
  577.         return mcrypt_create_iv(User::SALT_LENGTH, MCRYPT_DEV_URANDOM);
  578.     }
  579.    
  580.     //Generates a random session key with a pre-determined length
  581.     protected static function generateSessionKey()
  582.     {
  583.         $key = mcrypt_create_iv(User::SESSION_KEY_LENGTH, MCRYPT_DEV_URANDOM);
  584.         return hash(User::HASH_ALGORITHM, $key);
  585.     }
  586.    
  587.     //Generates a random confirmation code with a pre-determined length; result is hashed for email/url
  588.     protected static function generateConfirmCode()
  589.     {
  590.         $code = mcrypt_create_iv(User::CONFIRM_CODE_LENGTH, MCRYPT_DEV_URANDOM);
  591.         return sha1($code);
  592.     }
  593.    
  594.     //Sends out login cookies, with a few pre-defined parameters
  595.     protected static function sendCookies($username, $sessionKey)
  596.     {
  597.         if(User::COOKIE_SESSION_LENGTH == 0)
  598.             $expire = 0;
  599.         else
  600.             $expire = time() + User::COOKIE_SESSION_LENGTH;
  601.         setcookie('username',
  602.                   $username,
  603.                   $expire,
  604.                   User::COOKIE_PATH,
  605.                   User::COOKIE_DOMAIN,
  606.                   false,
  607.                   true);
  608.         setcookie('sessionKey',
  609.                   $sessionKey,
  610.                   $expire,
  611.                   User::COOKIE_PATH,
  612.                   User::COOKIE_DOMAIN,
  613.                   false,
  614.                   true);
  615.         $_COOKIE['username'] = $username;
  616.         $_COOKIE['sessionKey'] = $sessionKey;
  617.     }
  618.    
  619.     //Blanks login cookies, and removes them from the $_COOKIE array
  620.     protected static function removeCookies()
  621.     {
  622.         setcookie('username', NULL, -1);
  623.         setcookie('sessionKey', NULL, -1);
  624.         $_COOKIE['username'] = NULL;
  625.         $_COOKIE['sessionKey'] = NULL;
  626.     }
  627.    
  628.     //This method must be called to setup the database before any other code is called
  629.     public static function setupDB()
  630.     {
  631.         $db = new PDO('sqlite:'.User::DB_PATH);
  632.         //Create 'users' table...
  633.         $query = $db->prepare(
  634.         'CREATE TABLE IF NOT EXISTS users(id INTEGER PRIMARY KEY,
  635.                                           username TEXT NOT NULL UNIQUE COLLATE NOCASE,
  636.                                           password TEXT NOT NULL,
  637.                                           salt BLOB NOT NULL,
  638.                                           email TEXT NOT NULL UNIQUE COLLATE NOCASE,
  639.                                           date INTEGER NOT NULL,
  640.                                           sessionKey TEXT,
  641.                                           sessionIP TEXT)');
  642.         $query->execute();
  643.         //Create 'usersPending' table...
  644.         $query = $db->prepare(
  645.         'CREATE TABLE IF NOT EXISTS usersPending(id INTEGER PRIMARY KEY,
  646.                                     username TEXT NOT NULL UNIQUE COLLATE NOCASE,
  647.                                     password TEXT NOT NULL,
  648.                                     salt BLOB NOT NULL,
  649.                                     email TEXT NOT NULL UNIQUE COLLATE NOCASE,
  650.                                     date INTEGER NOT NULL,
  651.                                     confirmCode TEXT NOT NULL)');
  652.         $query->execute();
  653.         //Create 'usersChangeEmail' table...
  654.         $query = $db->prepare(
  655.         'CREATE TABLE IF NOT EXISTS usersChangeEmail(id INTEGER PRIMARY KEY,
  656.                                                      userID INTEGER UNIQUE NOT NULL,
  657.                                                      email TEXT NOT NULL UNIQUE COLLATE NOCASE,
  658.                                                      confirmCode TEXT NOT NULL)');
  659.                                                      //FOREIGN KEY UNITQUE NOT NULL (userID) REFERENCES users(id), //Requires sqlite version ~3.6? 3.3.7 available...
  660.         $query->execute();
  661.     }
  662. }
  663.  
  664. //CLASS SPECIFIC EXCEPTIONS FOLLOW
  665. class UserInvalidUsernameException extends InvalidArgumentException{
  666.     public function __construct($value){
  667.         parent::__construct('Invalid username: '.$value);
  668.     }
  669. }
  670. class UserInvalidPasswordException extends InvalidArgumentException{
  671.     public function __construct($value){
  672.         parent::__construct('Invalid password: '.$value);
  673.     }
  674. }
  675. class UserInvalidEmailException extends InvalidArgumentException{
  676.     public function __construct($value){
  677.         parent::__construct('Invalid email: '.$value);
  678.     }
  679. }
  680. class UserUnavailableUsernameException extends InvalidArgumentException{
  681.     public function __construct($value){
  682.         parent::__construct('Username \''.$value.'\' already exists in database.');
  683.     }
  684. }
  685. class UserUnavailableEmailException extends InvalidArgumentException{
  686.     public function __construct($value){
  687.         parent::__construct('Email \''.$value.'\' already exists in database.');
  688.     }
  689. }
  690.  
  691. ?>
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement