Guest User

Untitled

a guest
Jun 13th, 2015
289
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 37.77 KB | None | 0 0
  1. <?php
  2.  
  3. /**
  4.  * Класс авторизации и редактирования пользователя
  5.  * @author radiomonter [email protected]
  6.  
  7.  */
  8. class Login
  9. {
  10.     /**
  11.      * @object $db подключения к базе данных
  12.      */
  13.     private $db_connection = null;
  14.     /**
  15.      * @выбрать $user_id из таблицы user's по id
  16.      */
  17.     private $user_id = null;
  18.     /**
  19.      * @выбрать $user из таблицы user's Все
  20.      */
  21.     private $user_All = null;
  22.     /**
  23.      * @выбрать $user_name в таблице user's name(логин)
  24.      */
  25.     private $user_name = "";
  26.     /**
  27.      * @выбрать  $user_email в таблице user's email(почта)
  28.      */
  29.     private $user_email = "";
  30.     /**
  31.      * @выбрать  $user_names в таблице user's names(Имя)
  32.      */
  33.     private $user_names = "";
  34.         /**
  35.      * @выбрать  $user_surname Фамилии пользователя
  36.      */
  37.     private $user_surname = "";
  38.     /**
  39.      * @var boolean $user_is_logged_in The user's login status
  40.      */
  41.     private $user_is_logged_in = false;
  42.     /**
  43.      * @var string $user_gravatar_image_url The user's gravatar profile pic url (or a default one)
  44.      */
  45.     public $user_gravatar_image_url = "";
  46.     /**
  47.      * @var string $user_gravatar_image_tag The user's gravatar profile pic url with <img ... /> around
  48.      */
  49.     public $user_gravatar_image_tag = "http://s.gravatar.com/avatar/15d0c6d03817b10c05d38018993e235c?s=80&r=pg";
  50.     /**
  51.      * @var boolean $password_reset_link_is_valid Marker for view handling
  52.      */
  53.     private $password_reset_link_is_valid  = false;
  54.     /**
  55.      * @var boolean $password_reset_was_successful Marker for view handling
  56.      */
  57.     private $password_reset_was_successful = false;
  58.     /**
  59.      * Коллектор ошибок выводит ошибки в определенном методе
  60.      */
  61.     public $errors = array();
  62.     /**
  63.      * Коллектор Сообщений выводит сообщения в определенном методе
  64.      */
  65.     public $messages = array();
  66.  
  67.     /**
  68.      * the function "__construct()" automatically starts whenever an object of this class is created,
  69.      * you know, when you do "$login = new Login();"
  70.      */
  71.     public function __construct()
  72.     {
  73.         // create/read session
  74.         session_start();
  75.  
  76.         // TODO: organize this stuff better and make the constructor very small
  77.         // TODO: unite Login and Registration classes ?
  78.  
  79.         // check the possible login actions:
  80.         // 1. logout (happen when user clicks logout button)
  81.         // 2. login via session data (happens each time user opens a page on your php project AFTER he has successfully logged in via the login form)
  82.         // 3. login via cookie
  83.         // 4. login via post data, which means simply logging in via the login form. after the user has submit his login/password successfully, his
  84.         //    logged-in-status is written into his session data on the server. this is the typical behaviour of common login scripts.
  85.  
  86.         // if user tried to log out
  87.         if (isset($_GET["logout"])) {
  88.             $this->doLogout();
  89.  
  90.         // if user has an active session on the server
  91.         } elseif (!empty($_SESSION['user_name']) && ($_SESSION['user_logged_in'] == 1)) {
  92.             $this->loginWithSessionData();
  93.  
  94.             // checking for form submit from editing screen
  95.             // user try to change his username
  96.             if (isset($_POST["user_edit_submit_name"])) {
  97.                 // function below uses use $_SESSION['user_id'] et $_SESSION['user_email']
  98.                 $this->editUserName($_POST['user_name']);
  99.             // user try to change his email
  100.             } elseif (isset($_POST["user_edit_submit_email"])) {
  101.                 // function below uses use $_SESSION['user_id'] et $_SESSION['user_email']
  102.                 $this->editUserEmail($_POST['user_email']);
  103.             // user try to change his password
  104.             } elseif (isset($_POST["user_edit_submit_password"])) {
  105.                 // function below uses $_SESSION['user_name'] and $_SESSION['user_id']
  106.                 $this->editUserPassword($_POST['user_password_old'], $_POST['user_password_new'], $_POST['user_password_repeat']);
  107.             }
  108.  
  109.         // login with cookie
  110.         } elseif (isset($_COOKIE['rememberme'])) {
  111.             $this->loginWithCookieData();
  112.  
  113.         // if user just submitted a login form
  114.         } elseif (isset($_POST["login"])) {
  115.             if (!isset($_POST['user_rememberme'])) {
  116.                 $_POST['user_rememberme'] = null;
  117.             }
  118.             $this->loginWithPostData($_POST['user_name'], $_POST['user_password'], $_POST['user_rememberme']);
  119.         }
  120.  
  121.         // checking if user requested a password reset mail
  122.         if (isset($_POST["request_password_reset"]) && isset($_POST['user_name'])) {
  123.             $this->setPasswordResetDatabaseTokenAndSendMail($_POST['user_name']);
  124.         } elseif (isset($_GET["user_name"]) && isset($_GET["verification_code"])) {
  125.             $this->checkIfEmailVerificationCodeIsValid($_GET["user_name"], $_GET["verification_code"]);
  126.         } elseif (isset($_POST["submit_new_password"])) {
  127.             $this->editNewPassword($_POST['user_name'], $_POST['user_password_reset_hash'], $_POST['user_password_new'], $_POST['user_password_repeat']);
  128.         }
  129.  
  130.         // get gravatar profile picture if user is logged in
  131.         if ($this->isUserLoggedIn() == true) {
  132.             $this->getGravatarImageUrl($this->user_email);
  133.         }
  134.     }
  135.  
  136.     /**
  137.      * Checks if database connection is opened. If not, then this method tries to open it.
  138.      * @return bool Success status of the database connecting process
  139.      */
  140.     private function databaseConnection()
  141.     {
  142.         // if connection already exists
  143.         if ($this->db_connection != null) {
  144.             return true;
  145.         } else {
  146.             try {
  147.                 // Generate a database connection, using the PDO connector
  148.                 // @see http://net.tutsplus.com/tutorials/php/why-you-should-be-using-phps-pdo-for-database-access/
  149.                 // Also important: We include the charset, as leaving it out seems to be a security issue:
  150.                 // @see http://wiki.hashphp.org/PDO_Tutorial_for_MySQL_Developers#Connecting_to_MySQL says:
  151.                 // "Adding the charset to the DSN is very important for security reasons,
  152.                 // most examples you'll see around leave it out. MAKE SURE TO INCLUDE THE CHARSET!"
  153.                 $this->db_connection = new PDO('mysql:host='. DB_HOST .';dbname='. DB_NAME . ';charset=utf8', DB_USER, DB_PASS);
  154.                 return true;
  155.             } catch (PDOException $e) {
  156.                 $this->errors[] = MESSAGE_DATABASE_ERROR . $e->getMessage();
  157.             }
  158.         }
  159.         // default return
  160.         return false;
  161.     }
  162.  
  163.     /**
  164.      * Search into database for the user data of user_name specified as parameter
  165.      * @return user data as an object if existing user
  166.      * @return false if user_name is not found in the database
  167.      * TODO: @devplanete This returns two different types. Maybe this is valid, but it feels bad. We should rework this.
  168.      * TODO: @devplanete After some resarch I'm VERY sure that this is not good coding style! Please fix this.
  169.      */
  170.     private function getUsers_all($user_All) {
  171.         if ($this->databaseConnection()){
  172.             $query =$this->db_connection->prepare('SELECT * FROM users ;');
  173.             $result = $query->execute();
  174.             $rows = $query->fetchAll(PDO::FETCH_OBJ);
  175.  
  176. foreach($rows as $row);
  177.         }
  178.        
  179.     }
  180.     private function getUserData($user_name)
  181.     {
  182.         // iподключение к базе данных F открыт
  183.         if ($this->databaseConnection()) {
  184.             // запрос к базе данных, получать все данные выбранного user
  185.             $query_user = $this->db_connection->prepare('SELECT * FROM users WHERE user_name = :user_name');
  186.             $query_user->bindValue(':user_name', $user_name, PDO::PARAM_STR);
  187.             $query_user->execute();
  188.             // получить строку результата (в качестве объекта)
  189.             return $query_user->fetchObject();
  190.         } else {
  191.             return false;
  192.         }
  193.     }
  194.    
  195.        
  196.    
  197.     /**
  198.      * Журналы с данными в S_SESSION.
  199.      * Technically we are already logged in at that point of time, as the $_SESSION values already exist.
  200.      */
  201.     private function loginWithSessionData()
  202.     {
  203.         $this->user_name = $_SESSION['user_name'];
  204.         $this->user_email = $_SESSION['user_email'];
  205.         $this->user_names = $_SESSION['user_names'];
  206.         $this->user_surname = $_SESSION['user_surname'];
  207.  
  208.         // Набор вошли в статусе к истине, потому что мы только что проверил для этого:
  209.         // !empty($_SESSION['user_name']) && ($_SESSION['user_logged_in'] == 1)
  210.         // when we called this method (in the constructor)
  211.         $this->user_is_logged_in = true;
  212.     }
  213.  
  214.     /**
  215.      * Logs in via the Cookie
  216.      * @return bool success state of cookie login
  217.      */
  218.     private function loginWithCookieData()
  219.     {
  220.         if (isset($_COOKIE['rememberme'])) {
  221.             // extract data from the cookie
  222.             list ($user_id, $token, $hash) = explode(':', $_COOKIE['rememberme']);
  223.             // check cookie hash validity
  224.             if ($hash == hash('sha256', $user_id . ':' . $token . COOKIE_SECRET_KEY) && !empty($token)) {
  225.                 // cookie looks good, try to select corresponding user
  226.                 if ($this->databaseConnection()) {
  227.                     // get real token from database (and all other data)
  228.                     $sth = $this->db_connection->prepare("SELECT user_id, user_names, user_surname, user_name, user_email FROM users WHERE user_id = :user_id
  229.                                                      AND user_rememberme_token = :user_rememberme_token AND user_rememberme_token IS NOT NULL");
  230.                     $sth->bindValue(':user_id', $user_id, PDO::PARAM_INT);
  231.                     $sth->bindValue(':user_rememberme_token', $token, PDO::PARAM_STR);
  232.                     $sth->execute();
  233.                     // get result row (as an object)
  234.                     $result_row = $sth->fetchObject();
  235.  
  236.                     if (isset($result_row->user_id)) {
  237.                         // написать пользовательские данные в PHP сессии [файл на сервере]
  238.                         $_SESSION['user_id'] = $result_row->user_id;
  239.                         $_SESSION['user_names'] = $result_row->user_names;
  240.                         $_SESSION['user_surname'] = $result_row->user_surname;
  241.                         $_SESSION['user_name'] = $result_row->user_name;
  242.                         $_SESSION['user_email'] = $result_row->user_email;
  243.                         $_SESSION['user_logged_in'] = 1;
  244.  
  245.                         // объявить идентификатор пользователя, установите состояние входа к истинному
  246.                         $this->user_id = $result_row->user_id;
  247.                         $this->user_names = $result_row->user_names;
  248.                         $this->user_surname = $result_row->user_surname;
  249.                         $this->user_name = $result_row->user_name;
  250.                         $this->user_email = $result_row->user_email;
  251.                         $this->user_is_logged_in = true;
  252.  
  253.                         // Cookie token usable only once
  254.                         $this->newRememberMeCookie();
  255.                         return true;
  256.                     }
  257.                 }
  258.             }
  259.             // A cookie has been used but is not valid... we delete it
  260.             $this->deleteRememberMeCookie();
  261.             $this->errors[] = MESSAGE_COOKIE_INVALID;
  262.         }
  263.         return false;
  264.     }
  265.  
  266.     /**
  267.      * Журналы в с данными предоставляется в $ _POST, исходя из формы входа
  268.      * @param $user_name
  269.      * @param $user_password
  270.      * @param $user_rememberme
  271.      */
  272.     private function loginWithPostData($user_name, $user_password, $user_rememberme)
  273.     {
  274.         if (empty($user_name)) {
  275.             $this->errors[] = MESSAGE_USERNAME_EMPTY;
  276.         } else if (empty($user_password)) {
  277.             $this->errors[] = MESSAGE_PASSWORD_EMPTY;
  278.  
  279.         // if POST data (from login form) contains non-empty user_name and non-empty user_password
  280.         } else {
  281.             // user can login with his username or his email address.
  282.             // if user has not typed a valid email address, we try to identify him with his user_name
  283.             if (!filter_var($user_name, FILTER_VALIDATE_EMAIL)) {
  284.                 // database query, getting all the info of the selected user
  285.                 $result_row = $this->getUserData(trim($user_name));
  286.  
  287.             // if user has typed a valid email address, we try to identify him with his user_email
  288.             } else if ($this->databaseConnection()) {
  289.                 // database query, getting all the info of the selected user
  290.                 $query_user = $this->db_connection->prepare('SELECT * FROM users WHERE user_email = :user_email');
  291.                 $query_user->bindValue(':user_email', trim($user_name), PDO::PARAM_STR);
  292.                 $query_user->execute();
  293.                 // get result row (as an object)
  294.                 $result_row = $query_user->fetchObject();
  295.             }
  296.  
  297.             // if this user not exists
  298.             if (! isset($result_row->user_id)) {
  299.                 // was MESSAGE_USER_DOES_NOT_EXIST before, but has changed to MESSAGE_LOGIN_FAILED
  300.                 // to prevent potential attackers showing if the user exists
  301.                 $this->errors[] = MESSAGE_LOGIN_FAILED;
  302.             } else if (($result_row->user_failed_logins >= 3) && ($result_row->user_last_failed_login > (time() - 30))) {
  303.                 $this->errors[] = MESSAGE_PASSWORD_WRONG_3_TIMES;
  304.             // using PHP 5.5's password_verify() function to check if the provided passwords fits to the hash of that user's password
  305.             } else if (! password_verify($user_password, $result_row->user_password_hash)) {
  306.                 // increment the failed login counter for that user
  307.                 $sth = $this->db_connection->prepare('UPDATE users '
  308.                         . 'SET user_failed_logins = user_failed_logins+1, user_last_failed_login = :user_last_failed_login '
  309.                         . 'WHERE user_name = :user_name OR user_email = :user_name');
  310.                 $sth->execute(array(':user_name' => $user_name, ':user_last_failed_login' => time()));
  311.  
  312.                 $this->errors[] = MESSAGE_PASSWORD_WRONG;
  313.             // has the user activated their account with the verification email
  314.             } else if ($result_row->user_active != 1) {
  315.                 $this->errors[] = MESSAGE_ACCOUNT_NOT_ACTIVATED;
  316.             } else {
  317.                 // write user data into PHP SESSION [a file on your server]
  318.                 $_SESSION['user_id'] = $result_row->user_id;
  319.                 $_SESSION['user_names'] = $result_row->user_names;
  320.                 $_SESSION['user_surname'] = $result_row->user_surname;
  321.                 $_SESSION['user_name'] = $result_row->user_name;
  322.                 $_SESSION['user_email'] = $result_row->user_email;
  323.                 $_SESSION['user_logged_in'] = 1;
  324.  
  325.                 // declare user id, set the login status to true
  326.                 $this->user_id = $result_row->user_id;
  327.                 $this->user_names = $result_row->user_names;
  328.                 $this->user_surname = $result_row->user_surname;
  329.                 $this->user_name = $result_row->user_name;
  330.                 $this->user_email = $result_row->user_email;
  331.                 $this->user_is_logged_in = true;
  332.  
  333.                 // reset the failed login counter for that user
  334.                 $sth = $this->db_connection->prepare('UPDATE users '
  335.                         . 'SET user_failed_logins = 0, user_last_failed_login = NULL '
  336.                         . 'WHERE user_id = :user_id AND user_failed_logins != 0');
  337.                 $sth->execute(array(':user_id' => $result_row->user_id));
  338.  
  339.                 // if user has check the "remember me" checkbox, then generate token and write cookie
  340.                 if (isset($user_rememberme)) {
  341.                     $this->newRememberMeCookie();
  342.                 } else {
  343.                     // Reset remember-me token
  344.                     $this->deleteRememberMeCookie();
  345.                 }
  346.  
  347.                 // OPTIONAL: recalculate the user's password hash
  348.                 // DELETE this if-block if you like, it only exists to recalculate users's hashes when you provide a cost factor,
  349.                 // by default the script will use a cost factor of 10 and never change it.
  350.                 // check if the have defined a cost factor in config/hashing.php
  351.                 if (defined('HASH_COST_FACTOR')) {
  352.                     // check if the hash needs to be rehashed
  353.                     if (password_needs_rehash($result_row->user_password_hash, PASSWORD_DEFAULT, array('cost' => HASH_COST_FACTOR))) {
  354.  
  355.                         // calculate new hash with new cost factor
  356.                         $user_password_hash = password_hash($user_password, PASSWORD_DEFAULT, array('cost' => HASH_COST_FACTOR));
  357.  
  358.                         // TODO: this should be put into another method !?
  359.                         $query_update = $this->db_connection->prepare('UPDATE users SET user_password_hash = :user_password_hash WHERE user_id = :user_id');
  360.                         $query_update->bindValue(':user_password_hash', $user_password_hash, PDO::PARAM_STR);
  361.                         $query_update->bindValue(':user_id', $result_row->user_id, PDO::PARAM_INT);
  362.                         $query_update->execute();
  363.  
  364.                         if ($query_update->rowCount() == 0) {
  365.                             // writing new hash was successful. you should now output this to the user ;)
  366.                         } else {
  367.                             // writing new hash was NOT successful. you should now output this to the user ;)
  368.                         }
  369.                     }
  370.                 }
  371.             }
  372.         }
  373.     }
  374.  
  375.     /**
  376.      * Create all data needed for remember me cookie connection on client and server side
  377.      */
  378.     private function newRememberMeCookie()
  379.     {
  380.         // if database connection opened
  381.         if ($this->databaseConnection()) {
  382.             // generate 64 char random string and store it in current user data
  383.             $random_token_string = hash('sha256', mt_rand());
  384.             $sth = $this->db_connection->prepare("UPDATE users SET user_rememberme_token = :user_rememberme_token WHERE user_id = :user_id");
  385.             $sth->execute(array(':user_rememberme_token' => $random_token_string, ':user_id' => $_SESSION['user_id']));
  386.  
  387.             // generate cookie string that consists of userid, randomstring and combined hash of both
  388.             $cookie_string_first_part = $_SESSION['user_id'] . ':' . $random_token_string;
  389.             $cookie_string_hash = hash('sha256', $cookie_string_first_part . COOKIE_SECRET_KEY);
  390.             $cookie_string = $cookie_string_first_part . ':' . $cookie_string_hash;
  391.  
  392.             // set cookie
  393.             setcookie('rememberme', $cookie_string, time() + COOKIE_RUNTIME, "/", COOKIE_DOMAIN);
  394.         }
  395.     }
  396.  
  397.     /**
  398.      * Delete all data needed for remember me cookie connection on client and server side
  399.      */
  400.     private function deleteRememberMeCookie()
  401.     {
  402.         // if database connection opened
  403.         if ($this->databaseConnection()) {
  404.             // Reset rememberme token
  405.             $sth = $this->db_connection->prepare("UPDATE users SET user_rememberme_token = NULL WHERE user_id = :user_id");
  406.             $sth->execute(array(':user_id' => $_SESSION['user_id']));
  407.         }
  408.  
  409.         // set the rememberme-cookie to ten years ago (3600sec * 365 days * 10).
  410.         // that's obivously the best practice to kill a cookie via php
  411.         // @see http://stackoverflow.com/a/686166/1114320
  412.         setcookie('rememberme', false, time() - (3600 * 3650), '/', COOKIE_DOMAIN);
  413.     }
  414.  
  415.     /**
  416.      * Perform the logout, resetting the session
  417.      */
  418.     public function doLogout()
  419.     {
  420.         $this->deleteRememberMeCookie();
  421.  
  422.         $_SESSION = array();
  423.         session_destroy();
  424.  
  425.         $this->user_is_logged_in = false;
  426.         $this->messages[] = MESSAGE_LOGGED_OUT;
  427.     }
  428.  
  429.     /**
  430.      * Simply return the current state of the user's login
  431.      * @return bool user's login status
  432.      */
  433.     public function isUserLoggedIn()
  434.     {
  435.         return $this->user_is_logged_in;
  436.     }
  437.  
  438.     /**
  439.      * Edit the user's name, provided in the editing form
  440.      */
  441.     public function editUserName($user_name)
  442.     {
  443.         // prevent database flooding
  444.         $user_name = substr(trim($user_name), 0, 64);
  445.  
  446.         if (!empty($user_name) && $user_name == $_SESSION['user_name']) {
  447.             $this->errors[] = MESSAGE_USERNAME_SAME_LIKE_OLD_ONE;
  448.  
  449.         // username cannot be empty and must be azAZ09 and 2-64 characters
  450.         // TODO: maybe this pattern should also be implemented in Registration.php (or other way round)
  451.         } elseif (empty($user_name) || !preg_match("/^(?=.{2,64}$)[a-zA-Z][a-zA-Z0-9]*(?: [a-zA-Z0-9]+)*$/", $user_name)) {
  452.             $this->errors[] = MESSAGE_USERNAME_INVALID;
  453.  
  454.         } else {
  455.             // check if new username already exists
  456.             $result_row = $this->getUserData($user_name);
  457.  
  458.             if (isset($result_row->user_id)) {
  459.                 $this->errors[] = MESSAGE_USERNAME_EXISTS;
  460.             } else {
  461.                 // написать пользователя новые данные в базу данных
  462.                 $query_edit_user_name = $this->db_connection->prepare('UPDATE users SET user_name = :user_name WHERE user_id = :user_id');
  463.                 $query_edit_user_name->bindValue(':user_name', $user_name, PDO::PARAM_STR);
  464.                 $query_edit_user_name->bindValue(':user_id', $_SESSION['user_id'], PDO::PARAM_INT);
  465.                 $query_edit_user_name->execute();
  466.  
  467.                 if ($query_edit_user_name->rowCount()) {
  468.                     $_SESSION['user_name'] = $user_name;
  469.                     $this->messages[] = MESSAGE_USERNAME_CHANGED_SUCCESSFULLY . $user_name;
  470.                 } else {
  471.                     $this->errors[] = MESSAGE_USERNAME_CHANGE_FAILED;
  472.                 }
  473.             }
  474.         }
  475.     }
  476.  
  477.     /**
  478.      * Edit the user's email, provided in the editing form
  479.      */
  480.     public function editUserEmail($user_email)
  481.     {
  482.         // prevent database flooding
  483.         $user_email = substr(trim($user_email), 0, 64);
  484.  
  485.         if (!empty($user_email) && $user_email == $_SESSION["user_email"]) {
  486.             $this->errors[] = MESSAGE_EMAIL_SAME_LIKE_OLD_ONE;
  487.         // user mail cannot be empty and must be in email format
  488.         } elseif (empty($user_email) || !filter_var($user_email, FILTER_VALIDATE_EMAIL)) {
  489.             $this->errors[] = MESSAGE_EMAIL_INVALID;
  490.  
  491.         } else if ($this->databaseConnection()) {
  492.             // check if new email already exists
  493.             $query_user = $this->db_connection->prepare('SELECT * FROM users WHERE user_email = :user_email');
  494.             $query_user->bindValue(':user_email', $user_email, PDO::PARAM_STR);
  495.             $query_user->execute();
  496.             // get result row (as an object)
  497.             $result_row = $query_user->fetchObject();
  498.  
  499.             // если это письмо существует
  500.             if (isset($result_row->user_id)) {
  501.                 $this->errors[] = MESSAGE_EMAIL_ALREADY_EXISTS;
  502.             } else {
  503.                 // write users new data into database
  504.                 $query_edit_user_email = $this->db_connection->prepare('UPDATE users SET user_email = :user_email WHERE user_id = :user_id');
  505.                 $query_edit_user_email->bindValue(':user_email', $user_email, PDO::PARAM_STR);
  506.                 $query_edit_user_email->bindValue(':user_id', $_SESSION['user_id'], PDO::PARAM_INT);
  507.                 $query_edit_user_email->execute();
  508.  
  509.                 if ($query_edit_user_email->rowCount()) {
  510.                     $_SESSION['user_email'] = $user_email;
  511.                     $this->messages[] = MESSAGE_EMAIL_CHANGED_SUCCESSFULLY . $user_email;
  512.                 } else {
  513.                     $this->errors[] = MESSAGE_EMAIL_CHANGE_FAILED;
  514.                 }
  515.             }
  516.         }
  517.     }
  518.  
  519.     /**
  520.      * Edit the user's password, provided in the editing form
  521.      */
  522.     public function editUserPassword($user_password_old, $user_password_new, $user_password_repeat)
  523.     {
  524.         if (empty($user_password_new) || empty($user_password_repeat) || empty($user_password_old)) {
  525.             $this->errors[] = MESSAGE_PASSWORD_EMPTY;
  526.         // is the repeat password identical to password
  527.         } elseif ($user_password_new !== $user_password_repeat) {
  528.             $this->errors[] = MESSAGE_PASSWORD_BAD_CONFIRM;
  529.         // password need to have a minimum length of 6 characters
  530.         } elseif (strlen($user_password_new) < 6) {
  531.             $this->errors[] = MESSAGE_PASSWORD_TOO_SHORT;
  532.  
  533.         // all the above tests are ok
  534.         } else {
  535.             // database query, getting hash of currently logged in user (to check with just provided password)
  536.             $result_row = $this->getUserData($_SESSION['user_name']);
  537.  
  538.             // if this user exists
  539.             if (isset($result_row->user_password_hash)) {
  540.  
  541.                 // using PHP 5.5's password_verify() function to check if the provided passwords fits to the hash of that user's password
  542.                 if (password_verify($user_password_old, $result_row->user_password_hash)) {
  543.  
  544.                     // now it gets a little bit crazy: check if we have a constant HASH_COST_FACTOR defined (in config/hashing.php),
  545.                     // if so: put the value into $hash_cost_factor, if not, make $hash_cost_factor = null
  546.                     $hash_cost_factor = (defined('HASH_COST_FACTOR') ? HASH_COST_FACTOR : null);
  547.  
  548.                     // crypt the user's password with the PHP 5.5's password_hash() function, results in a 60 character hash string
  549.                     // the PASSWORD_DEFAULT constant is defined by the PHP 5.5, or if you are using PHP 5.3/5.4, by the password hashing
  550.                     // compatibility library. the third parameter looks a little bit shitty, but that's how those PHP 5.5 functions
  551.                     // want the parameter: as an array with, currently only used with 'cost' => XX.
  552.                     $user_password_hash = password_hash($user_password_new, PASSWORD_DEFAULT, array('cost' => $hash_cost_factor));
  553.  
  554.                     // write users new hash into database
  555.                     $query_update = $this->db_connection->prepare('UPDATE users SET user_password_hash = :user_password_hash WHERE user_id = :user_id');
  556.                     $query_update->bindValue(':user_password_hash', $user_password_hash, PDO::PARAM_STR);
  557.                     $query_update->bindValue(':user_id', $_SESSION['user_id'], PDO::PARAM_INT);
  558.                     $query_update->execute();
  559.  
  560.                     // check if exactly one row was successfully changed:
  561.                     if ($query_update->rowCount()) {
  562.                         $this->messages[] = MESSAGE_PASSWORD_CHANGED_SUCCESSFULLY;
  563.                     } else {
  564.                         $this->errors[] = MESSAGE_PASSWORD_CHANGE_FAILED;
  565.                     }
  566.                 } else {
  567.                     $this->errors[] = MESSAGE_OLD_PASSWORD_WRONG;
  568.                 }
  569.             } else {
  570.                 $this->errors[] = MESSAGE_USER_DOES_NOT_EXIST;
  571.             }
  572.         }
  573.     }
  574.  
  575.     /**
  576.      * Sets a random token into the database (that will verify the user when he/she comes back via the link
  577.      * in the email) and sends the according email.
  578.      */
  579.     public function setPasswordResetDatabaseTokenAndSendMail($user_name)
  580.     {
  581.         $user_name = trim($user_name);
  582.  
  583.         if (empty($user_name)) {
  584.             $this->errors[] = MESSAGE_USERNAME_EMPTY;
  585.  
  586.         } else {
  587.             // generate timestamp (to see when exactly the user (or an attacker) requested the password reset mail)
  588.             // btw this is an integer ;)
  589.             $temporary_timestamp = time();
  590.             // generate random hash for email password reset verification (40 char string)
  591.             $user_password_reset_hash = sha1(uniqid(mt_rand(), true));
  592.             // database query, getting all the info of the selected user
  593.             $result_row = $this->getUserData($user_name);
  594.  
  595.             // if this user exists
  596.             if (isset($result_row->user_id)) {
  597.  
  598.                 // database query:
  599.                 $query_update = $this->db_connection->prepare('UPDATE users SET user_password_reset_hash = :user_password_reset_hash,
  600.                                                               user_password_reset_timestamp = :user_password_reset_timestamp
  601.                                                               WHERE user_name = :user_name');
  602.                 $query_update->bindValue(':user_password_reset_hash', $user_password_reset_hash, PDO::PARAM_STR);
  603.                 $query_update->bindValue(':user_password_reset_timestamp', $temporary_timestamp, PDO::PARAM_INT);
  604.                 $query_update->bindValue(':user_name', $user_name, PDO::PARAM_STR);
  605.                 $query_update->execute();
  606.  
  607.                 // check if exactly one row was successfully changed:
  608.                 if ($query_update->rowCount() == 1) {
  609.                     // send a mail to the user, containing a link with that token hash string
  610.                     $this->sendPasswordResetMail($user_name, $result_row->user_email, $user_password_reset_hash);
  611.                     return true;
  612.                 } else {
  613.                     $this->errors[] = MESSAGE_DATABASE_ERROR;
  614.                 }
  615.             } else {
  616.                 $this->errors[] = MESSAGE_USER_DOES_NOT_EXIST;
  617.             }
  618.         }
  619.         // return false (this method only returns true when the database entry has been set successfully)
  620.         return false;
  621.     }
  622.  
  623.     /**
  624.      * Sends the password-reset-email.
  625.      */
  626.     public function sendPasswordResetMail($user_name, $user_email, $user_password_reset_hash)
  627.     {
  628.         $mail = new PHPMailer;
  629.  
  630.         // please look into the config/config.php for much more info on how to use this!
  631.         // use SMTP or use mail()
  632.         if (EMAIL_USE_SMTP) {
  633.             // Set mailer to use SMTP
  634.             $mail->IsSMTP();
  635.             //useful for debugging, shows full SMTP errors
  636.             //$mail->SMTPDebug = 1; // debugging: 1 = errors and messages, 2 = messages only
  637.             // Enable SMTP authentication
  638.             $mail->SMTPAuth = EMAIL_SMTP_AUTH;
  639.             // Enable encryption, usually SSL/TLS
  640.             if (defined(EMAIL_SMTP_ENCRYPTION)) {
  641.                 $mail->SMTPSecure = EMAIL_SMTP_ENCRYPTION;
  642.             }
  643.             // Specify host server
  644.             $mail->Host = EMAIL_SMTP_HOST;
  645.             $mail->Username = EMAIL_SMTP_USERNAME;
  646.             $mail->Password = EMAIL_SMTP_PASSWORD;
  647.             $mail->Port = EMAIL_SMTP_PORT;
  648.         } else {
  649.             $mail->IsMail();
  650.         }
  651.  
  652.         $mail->From = EMAIL_PASSWORDRESET_FROM;
  653.         $mail->FromName = EMAIL_PASSWORDRESET_FROM_NAME;
  654.         $mail->AddAddress($user_email);
  655.         $mail->Subject = EMAIL_PASSWORDRESET_SUBJECT;
  656.  
  657.         $link    = EMAIL_PASSWORDRESET_URL.'?user_name='.urlencode($user_name).'&verification_code='.urlencode($user_password_reset_hash);
  658.         $mail->Body = EMAIL_PASSWORDRESET_CONTENT . ' ' . $link;
  659.  
  660.         if(!$mail->Send()) {
  661.             $this->errors[] = MESSAGE_PASSWORD_RESET_MAIL_FAILED . $mail->ErrorInfo;
  662.             return false;
  663.         } else {
  664.             $this->messages[] = MESSAGE_PASSWORD_RESET_MAIL_SUCCESSFULLY_SENT;
  665.             return true;
  666.         }
  667.     }
  668.  
  669.     /**
  670.      * Checks if the verification string in the account verification mail is valid and matches to the user.
  671.      */
  672.     public function checkIfEmailVerificationCodeIsValid($user_name, $verification_code)
  673.     {
  674.         $user_name = trim($user_name);
  675.  
  676.         if (empty($user_name) || empty($verification_code)) {
  677.             $this->errors[] = MESSAGE_LINK_PARAMETER_EMPTY;
  678.         } else {
  679.             // database query, getting all the info of the selected user
  680.             $result_row = $this->getUserData($user_name);
  681.  
  682.             // if this user exists and have the same hash in database
  683.             if (isset($result_row->user_id) && $result_row->user_password_reset_hash == $verification_code) {
  684.  
  685.                 $timestamp_one_hour_ago = time() - 3600; // 3600 seconds are 1 hour
  686.  
  687.                 if ($result_row->user_password_reset_timestamp > $timestamp_one_hour_ago) {
  688.                     // set the marker to true, making it possible to show the password reset edit form view
  689.                     $this->password_reset_link_is_valid = true;
  690.                 } else {
  691.                     $this->errors[] = MESSAGE_RESET_LINK_HAS_EXPIRED;
  692.                 }
  693.             } else {
  694.                 $this->errors[] = MESSAGE_USER_DOES_NOT_EXIST;
  695.             }
  696.         }
  697.     }
  698.  
  699.     /**
  700.      * Checks and writes the new password.
  701.      */
  702.     public function editNewPassword($user_name, $user_password_reset_hash, $user_password_new, $user_password_repeat)
  703.     {
  704.         // TODO: timestamp!
  705.         $user_name = trim($user_name);
  706.  
  707.         if (empty($user_name) || empty($user_password_reset_hash) || empty($user_password_new) || empty($user_password_repeat)) {
  708.             $this->errors[] = MESSAGE_PASSWORD_EMPTY;
  709.         // is the repeat password identical to password
  710.         } else if ($user_password_new !== $user_password_repeat) {
  711.             $this->errors[] = MESSAGE_PASSWORD_BAD_CONFIRM;
  712.         // password need to have a minimum length of 6 characters
  713.         } else if (strlen($user_password_new) < 6) {
  714.             $this->errors[] = MESSAGE_PASSWORD_TOO_SHORT;
  715.         // if database connection opened
  716.         } else if ($this->databaseConnection()) {
  717.             // now it gets a little bit crazy: check if we have a constant HASH_COST_FACTOR defined (in config/hashing.php),
  718.             // if so: put the value into $hash_cost_factor, if not, make $hash_cost_factor = null
  719.             $hash_cost_factor = (defined('HASH_COST_FACTOR') ? HASH_COST_FACTOR : null);
  720.  
  721.             // crypt the user's password with the PHP 5.5's password_hash() function, results in a 60 character hash string
  722.             // the PASSWORD_DEFAULT constant is defined by the PHP 5.5, or if you are using PHP 5.3/5.4, by the password hashing
  723.             // compatibility library. the third parameter looks a little bit shitty, but that's how those PHP 5.5 functions
  724.             // want the parameter: as an array with, currently only used with 'cost' => XX.
  725.             $user_password_hash = password_hash($user_password_new, PASSWORD_DEFAULT, array('cost' => $hash_cost_factor));
  726.  
  727.             // write users new hash into database
  728.             $query_update = $this->db_connection->prepare('UPDATE users SET user_password_hash = :user_password_hash,
  729.                                                           user_password_reset_hash = NULL, user_password_reset_timestamp = NULL
  730.                                                           WHERE user_name = :user_name AND user_password_reset_hash = :user_password_reset_hash');
  731.             $query_update->bindValue(':user_password_hash', $user_password_hash, PDO::PARAM_STR);
  732.             $query_update->bindValue(':user_password_reset_hash', $user_password_reset_hash, PDO::PARAM_STR);
  733.             $query_update->bindValue(':user_name', $user_name, PDO::PARAM_STR);
  734.             $query_update->execute();
  735.  
  736.             // check if exactly one row was successfully changed:
  737.             if ($query_update->rowCount() == 1) {
  738.                 $this->password_reset_was_successful = true;
  739.                 $this->messages[] = MESSAGE_PASSWORD_CHANGED_SUCCESSFULLY;
  740.             } else {
  741.                 $this->errors[] = MESSAGE_PASSWORD_CHANGE_FAILED;
  742.             }
  743.         }
  744.     }
  745.  
  746.     /**
  747.      * Gets the success state of the password-reset-link-validation.
  748.      * TODO: should be more like getPasswordResetLinkValidationStatus
  749.      * @return boolean
  750.      */
  751.     public function passwordResetLinkIsValid()
  752.     {
  753.         return $this->password_reset_link_is_valid;
  754.     }
  755.  
  756.     /**
  757.      * Gets the success state of the password-reset action.
  758.      * TODO: should be more like getPasswordResetSuccessStatus
  759.      * @return boolean
  760.      */
  761.     public function passwordResetWasSuccessful()
  762.     {
  763.         return $this->password_reset_was_successful;
  764.     }
  765.  
  766.     /**
  767.      * Gets the username
  768.      * @return string username
  769.      */
  770.     public function getUsername()
  771.     {
  772.         return $this->user_name;
  773.     }
  774.  
  775.     /**
  776.      * Get either a Gravatar URL or complete image tag for a specified email address.
  777.      * Gravatar is the #1 (free) provider for email address based global avatar hosting.
  778.      * The URL (or image) returns always a .jpg file !
  779.      * For deeper info on the different parameter possibilities:
  780.      * @see http://de.gravatar.com/site/implement/images/
  781.      *
  782.      * @param string $email The email address
  783.      * @param string $s Size in pixels, defaults to 50px [ 1 - 2048 ]
  784.      * @param string $d Default imageset to use [ 404 | mm | identicon | monsterid | wavatar ]
  785.      * @param string $r Maximum rating (inclusive) [ g | pg | r | x ]
  786.      * @param array $atts Optional, additional key/value attributes to include in the IMG tag
  787.      * @source http://gravatar.com/site/implement/images/php/
  788.      */
  789.     public function getGravatarImageUrl($email, $s = 50, $d = 'mm', $r = 'g', $atts = array() )
  790.     {
  791.         $url = 'http://www.gravatar.com/avatar/';
  792.         $url .= md5(strtolower(trim($email)));
  793.         $url .= "?s=$s&d=$d&r=$r&f=y";
  794.  
  795.         // the image url (on gravatarr servers), will return in something like
  796.         // http://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50?s=80&d=mm&r=g
  797.         // note: the url does NOT have something like .jpg
  798.         $this->user_gravatar_image_url = "http://s.gravatar.com/avatar/15d0c6d03817b10c05d38018993e235c?s=80&r=pg";
  799.  
  800.         // build img tag around
  801.         $url = '<img src="' . $url . '"';
  802.         foreach ($atts as $key => $val)
  803.             $url .= ' ' . $key . '="' . $val . '"';
  804.         $url .= ' />';
  805.  
  806.         // the image url like above but with an additional <img src .. /> around
  807.         $this->user_gravatar_image_tag =  $url ;
  808.     }
  809. }
Advertisement
Add Comment
Please, Sign In to add comment