Jump to content

I have introduced active and inactive user concept, In database it is changing 0 & 1 in status colum But redirection to page when inactive is not work


jatin333

Recommended Posts

My Ls file code is

 

<?php
namespace Fr;

/**
.---------------------------------------------------------------------------.
| The Francium Project                                                      |
| ------------------------------------------------------------------------- |
| This software logSys is a part of the Francium (Fr) project.              |
| [url=http://subinsb.com/the-francium-project]http://subinsb.com/the-francium-project[/url]                                   |
| ------------------------------------------------------------------------- |
|     Author: Subin Siby                                                    |
| Copyright (c) 2014 - 2015, Subin Siby. All Rights Reserved.               |
| ------------------------------------------------------------------------- |
|   License: Distributed under the Apache License, Version 2.0              |
|            [url=http://www.apache.org/licenses/LICENSE-2.0]http://www.apache.org/licenses/LICENSE-2.0[/url]                     |
| This program is distributed in the hope that it will be useful - WITHOUT  |
| ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or     |
| FITNESS FOR A PARTICULAR PURPOSE.                                         |
'---------------------------------------------------------------------------'
*/

/**
.---------------------------------------------------------------------------.
|  Software:      PHP Login System - PHP logSys                             |
|  Version:       0.8.2                                                     |
|  Documentation: [url=http://subinsb.com/php-logsys]http://subinsb.com/php-logsys[/url]                             |
|  Contribute:    [url=https://github.com/subins2000/logSys]https://github.com/subins2000/logSys[/url]                      |
'---------------------------------------------------------------------------'
*/

class LS {

  /**
   * @var array Default configuration
   */
  public static $default_config = array(
    /**
     * Basic Config of logSys
     */
    "basic" => array(
      "company" => "My Site",
      "email" => "email@mysite.com",
      "email_callback" => false,

      /**
       * Callback to override output content
       */
      "output_callback" => false
    ),

    /**
     * Database Configuration
     */
    "db" => array(
      /**
       * @var string "mysql" or "postgresql" or "sqlite"
       */
      "type" => "mysql",

      /**
       * MySQL options
       */
      "host" => "",
      "port" => "3306",

      /**
       * SQLite options
       */
      "sqlite_path" => "",

      "username" => "",
      "password" => "",

      "name" => "",
      "table" => "users",
      "token_table" => "user_tokens",

      "columns" => array(
        "id" => "id",
        "username" => "username",
        "password" => "password",
        "email" => "email",
        "attempt" => "attempt"
      ),
    ),

    /**
     * Keys used for encryption
     * DONT MAKE THIS PUBLIC
     */
    "keys" => array(
      /**
       * Changing cookie key will expire all current active login sessions
       */
      "cookie" => "ckxc436jd*^30f840v*9!@#$",
      /**
       * `salt` should not be changed after users are created
       */
      "salt" => "^#$4%9f+1^p9)M@4M)V$"
    ),

    /**
     * Enable/Disable certain features
     */
    "features" => array(
      /**
       * Should I Call session_start();
       */
      "start_session" => true,
      /**
       * Enable/Disable Login using Username & E-Mail
       */
      "email_login" => true,
      /**
       * Enable/Disable `Remember Me` feature
       */
      "remember_me" => true,
      /**
       * Should \Fr\LS::init() be called automatically
       */
      "auto_init" => false,

      /**
       * Prevent Brute Forcing
       * ---------------------
       * By enabling this, logSys will deny login for the time mentioned
       * in the "brute_force"->"time_limit" seconds after "brute_force"->"tries"
       * number of incorrect login tries.
       */
      "block_brute_force" => true,

      /**
       * Two Step Login
       * --------------
       * By enabling this, a checking is done when user visits
       * whether the device he/she uses is approved by the user.
       * Allows the original user to revoke logins in other devices/places
       * Useful if the user forgot to logout in some place.
       */
      "two_step_login" => false
    ),

    /**
     * `Blocking Brute Force Attacks` options
     */
    "brute_force" => array(
      /**
       * No of tries alloted to each user
       */
      "tries" => 5,
      /**
       * The time IN SECONDS for which block from login action should be done after
       * incorrect login attempts. Use [url=http://www.easysurf.cc/utime.htm#m60s]http://www.easysurf.cc/utime.htm#m60s[/url]
       * for converting minutes to seconds. Default : 5 minutes
       */
      "time_limit" => 300
    ),

    /**
     * Information about pages
     */
    "pages" => array(
      /**
       * Pages that doesn't require logging in.
       * Exclude login page, but include REGISTER page.
       * Use RELATIVE links. To find the relative link of
       * a page, do var_dump(Fr\LS::curPage());
       */
      "no_login" => array(),

      /**
       * Pages that both logged in and not logged in users can access
       */
      "everyone" => array(),

      /**
       * The login page. ex : /login.php or /accounts/login.php
       */
      "login_page" => "",

      /**
       * The home page. The main page for logged in users.
       * logSys redirects to here after user logs in
       */
      "home_page" => ""
    ),

    /**
     * Settings about cookie creation
     */
    "cookies" => array(
      /**
       * Default : cookies expire in 30 days. The value is
       * for setting in strtotime() function
       * [url=http://php.net/manual/en/function.strtotime.php]http://php.net/manual/en/function.strtotime.php[/url]
       */
      "expire" => "+30 days",
      "path" => "/",
      "domain" => "",

      /**
       * Names of cookies created
       */
      "names" => array(
        "login_token" => "lg",
        "remember_me" => "rm",
        "device" => "dv",

        /**
         * This is used as $_SESSION key
         */
        "current_user" => "cu",
        "device_verified" => "dvv"
      ),
    ),

    /**
     * 2 Step Login
     */
    'two_step_login' => array(
      /**
       * Message to show before displaying "Enter Token" form.
       */
      'instruction' => '',

      /**
       * Callback when token is generated.
       * Used to send message to user (Phone/E-Mail)
       */
      'send_callback' => '',

      /**
       * The table to stoe user's sessions
       */
      'devices_table' => 'user_devices',

      /**
       * The length of token generated.
       * A low value is better for tokens sent via Mobile SMS
       */
      'token_length' => 4,

      /**
       * Whether the token should be numeric only ?
       * Default Token : Alphabetic + Numeric mixed strings
       */
      'numeric' => false,

      /**
       * The expire time of cookie that authorizes the device
       * to login using the user's account with 2 Step Verification
       * The value is for setting in strtotime() function
       * [url=http://php.net/manual/en/function.strtotime.php]http://php.net/manual/en/function.strtotime.php[/url]
       */
      'expire' => '+6 days',

      /**
       * Should logSys checks if device is valid, everytime
       * logSys is initiated ie everytime a page loads
       * If you want to check only the first time a user loads
       * a page, then set the value to TRUE, else FALSE
       */
      'first_check_only' => true
    ),

    /**
     * Debug info
     */
    "debug" => array(
      /**
       * Enable debugging
       */
      "enable" => false,

      /**
       * Absolute path
       */
      "log_file" => "",
    )
  );

  /**
   * @var array
   */
  private $config = array();

  /**
   * Config
   * @param  array  $config [description]
   * @return [type]         [description]
   */
  public function config($config = array()){
    /**
     * Callback to display messages for different states
     * @var array
     */
    self::$default_config["basic"]["output_callback"] = function(&$LS, $state, $extraInfo = array()){
      if($state === "invalidToken")
        return "<h3>Error : Wrong/Invalid Token</h3>";
      else if($state === "fieldsLeftBlank")
        return "<h3>Error : Fields Left Blank</h3>";
      else if($state === "passwordDontMatch")
        return "<h3>Error : Passwords Don't Match</h3>";
      else if($state === "passwordChanged")
        return "<h3>Success : Password Reset Successful</h3><p>You may now login with your new password.</p>";
      else if($state === "identityNotProvided")
        return "<h3>Error : {$extraInfo["identity_type"]} not provided</h3>";
      else if($state === "userNotFound")
        return "<h3>Error : User Not Found</h3>";
      else if($state === "notLoggedIn")
        return "<h3>Error : Not Logged In</h3>";
      else if($state === "twoStepLoginVerifyForm")
        return "<form action='". self::curPageURL() ."' method='POST'>
          <p>". $LS->config['two_step_login']['instruction'] ."</p>
          <label>
            <p>Token Received</p>
            <input type='text' name='logSys_two_step_login-token' placeholder='Paste the token here... (case sensitive)' />
          </label>
          <label style='display: block;'>
            <span>Remember this device ?</span>
            <input type='checkbox' name='logSys_two_step_login-dontask' />
          </label>
          <input type='hidden' name='logSys_two_step_login-uid' value='". $extraInfo["uid"] ."' />
          ". ($extraInfo["remember_me"] === true ? "<input type='hidden' name='logSys_two_step_login-remember_me' />" : "") ."
          <label>
            <button>Verify</button>
          </label>
        </form>";
      else if($state === "resetPasswordRequestForm")
        return "<form action='". self::curPageURL() ."' method='POST'>
          <label>
            <p>{$extraInfo["identity_type"]}</p>
            <input type='text' id='logSysIdentification' placeholder='Enter your {$extraInfo["identity_type"]}' size='25' name='identification' />
          </label>
          <p><button name='logSysForgotPass' type='submit'>Reset Password</button></p>
        </form>";
      else if($state === "resetPasswordForm")
        return "<p>The Token key was Authorized. Now, you can change the password</p>
          <form action='". self::curPageURL() ."' method='POST'>
            <input type='hidden' name='token' value='{$extraInfo["resetPassToken"]}' />
            <label>
              <p>New Password</p>
              <input type='password' name='logSysForgotPassNewPassword' />
            </label><br/>
            <label>
              <p>Retype Password</p>
              <input type='password' name='logSysForgotPassRetypedPassword'/>
            </label><br/>
            <p><button name='logSysForgotPassChange'>Reset Password</button></p>
          </form>";
    };

    $this->config = array_replace_recursive(self::$default_config, $config);

    /**
     * Add the login page to the array of pages that doesn't need logging in
     */
    array_push($this->config['pages']['no_login'], $this->config['pages']['login_page']);

    if( $this->config["debug"]["enable"] ){
      ini_set("display_errors", "on");
    }
  }

  /**
   * Add messages to log file
   *
   * @param  string  $msg Message
   * @return boolean      Whether message was written
   */
  public function log($msg = ""){
    if( $this->config["debug"]["enable"] ){
      $log_file = $this->config["debug"]["log_file"];

      if( $log_file === "" )
        $log_file = __DIR__ . "/Francium.log";

      if( $msg !== "" ){
        $message = "[" . date("Y-m-d H:i:s") . "] $msg";
        $fh = fopen($log_file, 'a');
        fwrite($fh, $message . "\n");
        fclose($fh);
        return true;
      }
    }
    return false;
  }

  /**
   * @var boolean Is user logged in
   */
  public $loggedIn = false;

  /**
   * @var int|boolean User ID
   */
  public $userID = false;

  /**
   * @var \PDO Database handler
   */
  private $dbh;

  /**
   * @var boolean Whether Fr\LS::init() was called
   */
  private $initCalled = false;

  /**
   * Intialize
   * @param array $config Configuration
   */
  public function __construct($config = array()){
    $this->config( $config );

    if( $this->config['features']['start_session'] === true ){
      session_start();
    }

    /**
    * Try connecting to Database Server
    */
    try{
      if( $this->config["db"]["type"] === "sqlite" ) {

        $this->dbh = new \PDO("sqlite:" . $this->config["db"]["sqlite_path"],
          $this->config["db"]["username"],
          $this->config["db"]["password"],
          array(
            \PDO::ATTR_PERSISTENT => true,
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
          )
        );

        /**
         * Enable Multithreading Read/Write
         */
        $this->dbh->exec("PRAGMA journal_mode=WAL;");

      } else if( $this->config["db"]["type"] === "postgresql" ) {

        $this->dbh = new \PDO("pgsql:dbname=". $this->config['db']['name'] .";host=". $this->config['db']['host'] .";port=". $this->config['db']['port']. ";",
          $this->config['db']['username'],
          $this->config['db']['password'],
          array(
            \PDO::ATTR_PERSISTENT => true,
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
          )
        );

      } else {

        $this->dbh = new \PDO("mysql:dbname=". $this->config['db']['name'] .";host=". $this->config['db']['host'] .";port=". $this->config['db']['port']. ";charset=utf8",
          $this->config['db']['username'],
          $this->config['db']['password'],
          array(
            \PDO::ATTR_PERSISTENT => true,
            \PDO::ATTR_ERRMODE => \PDO::ERRMODE_EXCEPTION
        ));

      }

      $cookieToken = isset($_COOKIE[$this->config['cookies']['names']['login_token']]) ? $_COOKIE[ $this->config['cookies']['names']['login_token'] ] : false;

      /**
       * @var int|boolean User ID is stored in session
       */
      $sessionUID = isset( $_SESSION[ $this->config['cookies']['names']['current_user'] ] ) ? $_SESSION[ $this->config['cookies']['names']['current_user'] ] : false;

      $rememberMe = isset( $_COOKIE[ $this->config['cookies']['names']['remember_me'] ] ) ? $_COOKIE[ $this->config['cookies']['names']['remember_me'] ] : false;

      if($cookieToken) {
        $loginToken = hash("sha256", $this->config['keys']['cookie'] . $sessionUID . $this->config['keys']['cookie']);

        $this->loggedIn = $cookieToken === $loginToken;
      }

      /**
      * If there is a Remember Me Cookie and the user is not logged in,
      * then log in the user with the ID in the remember cookie if it
      * matches with the decrypted value in `logSyslogin` cookie
      */
      if( $this->config['features']['remember_me'] === true && $rememberMe && !$this->loggedIn ) {
        $loginToken = hash("sha256", $this->config['keys']['cookie']. $rememberMe . $this->config['keys']['cookie']);

        $this->loggedIn = $loginToken === $loginToken;

        if($this->loggedIn === true){
          $_SESSION[$this->config["cookies"]["names"]["current_user"]] = $rememberMe;
          $sessionUID = $rememberMe;
        }
      }

      $this->userID = $sessionUID;

      /**
       * Check if devices is authorized to use the account
       */
      if($this->config['features']['two_step_login'] === true && $this->loggedIn){
        $login_page = self::curPage() === $this->config['pages']['login_page'];

        if(!isset($_SESSION[ $this->config['cookies']['names']['device_verified'] ]) && !isset($_COOKIE[$this->config["cookies"]["names"]["device"]]) && $login_page === false){
          /**
           * The device cookie is not even set. So, logout
           */
          $this->logout();
        }else if($this->config['two_step_login']['first_check_only'] === false || ($this->config['two_step_login']['first_check_only'] === true && !isset($_SESSION[ $this->config['cookies']['names']['device_verified'] ]))){
          $sql = $this->dbh->prepare("SELECT '1' FROM ". $this->config['two_step_login']['devices_table'] ." WHERE uid = ? AND token = ?");
          $sql->execute(array($this->userID, $_COOKIE[$this->config["cookies"]["names"]["device"]]));

          /**
           * Device not authorized, so remove device cookie & logout
           */
          if($sql->fetchColumn() !== '1' && $login_page === false){
            setcookie($this->config["cookies"]["names"]["device"], "", time() - 10);
            $this->logout();
          }else{
            /**
             * This session has been checked and verified
             */
            $_SESSION[ $this->config['cookies']['names']['device_verified'] ] = 1;
          }
        }
      }

      if( $this->config['features']['auto_init'] === true ){
        $this->init();
      }
      return true;
    }catch(\PDOException $e) {
      /**
       * Couldn't connect to Database
       */
      self::log("Could not connect to database. Check config->db credentials. PDO Output: " . $e->getMessage());
      return false;
    }
  }

  /**
   * A function that will automatically redirect user according to his/her login status
   * @return void
   */
  public function init() {
    if(in_array(self::curPage(), $this->config['pages']['everyone'])){
      /**
       * No redirects as this page can be accessed
       * by anyone whether he/she is logged in or not
       */
    }else if($this->loggedIn === true && in_array(self::curPage(), $this->config['pages']['no_login'])){
      self::redirect($this->config['pages']['home_page']);
    }else if($this->loggedIn === false && array_search(self::curPage(), $this->config['pages']['no_login']) === false){
      self::redirect($this->config['pages']['login_page']);
    }
    $this->initCalled = true;
  }

  /**
   * Login the user
   * @param  string               $username    Username or email
   * @param  boolean|string       $password    Password as string or
   *                                           FALSE for skipping password check
   * @param  boolean              $remember_me Remember me
   * @param  boolean              $cookies     Should cookies be created and redirected
   * @return array|boolean|string              Array if user is blocked
   *                                           TRUE if login was success
   *                                           User ID if login was success and $cookies FALSE
   */
  public function login($username, $password, $remember_me = false, $cookies = true){
    /**
     * We Add LIMIT to 1 in SQL query because to
     * get an array with key as the column name.
     */
    if($this->config['features']['email_login'] === true){
      $query = "SELECT ". $this->config["db"]["columns"]["id"] .", ". $this->config["db"]["columns"]["password"] .", ". $this->config["db"]["columns"]["attempt"] ." FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login OR ". $this->config["db"]["columns"]["email"] ."=:login ORDER BY ". $this->config["db"]["columns"]["id"] ." LIMIT 1";
    }else{
      $query = "SELECT ". $this->config["db"]["columns"]["id"] .", ". $this->config["db"]["columns"]["password"] .", ". $this->config["db"]["columns"]["attempt"] ." FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login ORDER BY ". $this->config["db"]["columns"]["id"] ." LIMIT 1";
    }

    $sql = $this->dbh->prepare($query);
    $sql->bindValue(":login", $username);

    $sql->execute();
    $cols = $sql->fetch(\PDO::FETCH_ASSOC);

    if(empty($cols)){
      // No such user like that
      return false;
    }else{
      /**
       * Get the user details
       */
      $us_id = $cols['id'];
      $us_pass = $cols['password'];
      $status = $cols['attempt'];
    
      
      if(substr($status, 0, 2) == "b-"){
        $blockedTime = substr($status, 2);
        if(time() < $blockedTime){
          $blocked = true;
          return array(
            "status"  => "blocked",
            "minutes" => round(abs($blockedTime - time()) / 60, 0),
            "seconds" => round(abs($blockedTime - time()) / 60*60, 2)
            );
            
        }else{
          // remove the block, because the time limit is over
          $this->updateUser(array(
            "attempt" => "" // No tries at all
          ), $us_id);
        }
      }
      else {
             $stat = $cols['status'];
          if $stat=0;{
        $deactive = true;
          $this->loggedIn = true;
          return array(
          "stat"  => "deactive"
             );
        }
        $this->initCalled = true;
        }
      if( !isset($blocked) && ($password === false || password_verify($password . $this->config['keys']['salt'], $us_pass) ) ){
        if($cookies === true){

          $_SESSION[$this->config["cookies"]["names"]["current_user"]] = $us_id;

          setcookie($this->config["cookies"]["names"]["login_token"], hash("sha256", $this->config['keys']['cookie'] . $us_id . $this->config['keys']['cookie']), strtotime($this->config['cookies']['expire']), $this->config['cookies']['path'], $this->config['cookies']['domain']);

          if( $remember_me === true && $this->config['features']['remember_me'] === true ){
            setcookie($this->config["cookies"]["names"]["remember_me"], $us_id, strtotime($this->config['cookies']['expire']), $this->config['cookies']['path'], $this->config['cookies']['domain']);
          }
        
          if($this->config['features']['block_brute_force'] === true){
            /**
             * If Brute Force Protection is Enabled,
             * Reset the attempt status
             */
            $this->updateUser(array(
              "attempt" => "0"
            ), $us_id);
          }

          // Redirect
          if($this->initCalled){
            self::redirect($this->config['pages']['home_page']);
          }
          return true;
        }else{
          /**
           * If cookies shouldn't be set,
           * it means login() was called
           * to get the user's ID. So, return it
           */
          return $us_id;
        }
      }else{
        /**
         * Incorrect password
         * ------------------
         * Check if brute force protection is enabled
         */
        if($this->config['features']['block_brute_force'] === true){
          $max_tries = $this->config['brute_force']['tries'];

          if($status == ""){
            // User was not logged in before
            $this->updateUser(array(
              "attempt" => "1" // Tried 1 time
            ), $us_id);
          }else if($status == $max_tries){
            /**
             * Account Blocked. User will be only able to
             * re-login at the time in UNIX timestamp
             */
            $eligible_for_next_login_time = strtotime("+". $this->config['brute_force']['time_limit'] ." seconds", time());
            $this->updateUser(array(
              "attempt" => "b-" . $eligible_for_next_login_time
            ), $us_id);
            return array(
              "status"  => "blocked",
              "minutes" => round(abs($eligible_for_next_login_time - time()) / 60, 0),
              "seconds" => round(abs($eligible_for_next_login_time - time()) / 60*60, 2)
            );
          }else if($status < $max_tries){
            // If the attempts are less than Max and not Max
            $this->updateUser(array(
              "attempt" => $status + 1 // Increase the no of tries by +1.
            ), $us_id);
          }
        }
        return false;
      }
    }
  }

  /**
   * Register a user
   * @param  string         $identification Username or email
   * @param  string         $password       Password
   * @param  array          $other          Values for other columns
   * @return boolean|string                 Whether user exists (exists) or account was created
   */
  public function register( $identification, $password, $other = array() ){
    if( $this->userExists($identification) || (isset($other['email']) && $this->userExists($other['email'])) ){
      return "exists";
    }else{
      $hashedPass = password_hash($password. $this->config['keys']['salt'], PASSWORD_DEFAULT);

      if( count($other) == 0 ){
        /* If there is no other fields mentioned, make the default query */
        $sql = $this->dbh->prepare("INSERT INTO ". $this->config['db']['table'] ." (". $this->config["db"]["columns"]["username"] .", ". $this->config["db"]["columns"]["password"] .") VALUES(:username, :password)");
      }else{
        /* if there are other fields to add value to, make the query and bind values according to it */
        $keys   = array_keys($other);
        $columns = implode(",", $keys);
        $colVals = implode(",:", $keys);
        $sql   = $this->dbh->prepare("INSERT INTO ". $this->config['db']['table'] ." (". $this->config["db"]["columns"]["username"] .", ". $this->config["db"]["columns"]["password"] .", $columns) VALUES(:username, :password, :$colVals)");
        foreach($other as $key => $value){
          $value = htmlspecialchars($value);
          $sql->bindValue(":$key", $value);
        }
      }
      /* Bind the default values */
      $sql->bindValue(":username", $identification);
      $sql->bindValue(":password", $hashedPass);
      $sql->execute();
      return true;
    }
  }

  /**
   * Logout the user by destroying the cookies and session
   */
  public function logout(){
    session_destroy();
    setcookie($this->config["cookies"]["names"]["login_token"], "", time() - 10, $this->config['cookies']['path'], $this->config['cookies']['domain']);
    setcookie($this->config["cookies"]["names"]["remember_me"], "", time() - 10, $this->config['cookies']['path'], $this->config['cookies']['domain']);

    /**
     * Wait for the cookies to be removed, then redirect
     */
    usleep(2000);
    self::redirect($this->config['pages']['login_page']);
    return true;
  }

  /**
   * A function to handle the Forgot Password process
   * @return string The current state of the process
   */
  public function forgotPassword(){
    $curStatus = "initial";  // The Current Status of Forgot Password process
    $identName = $this->config['features']['email_login'] === false ? "Username" : "Username / E-Mail";

    if( !isset($_POST['logSysForgotPass']) && !isset($_GET['resetPassToken']) && !isset($_POST['logSysForgotPassChange']) ){
      echo $this->getOutput("resetPasswordRequestForm", array(
        "identity_type" => $identName
      ));

      /**
       * The user had moved to the reset password form ie she/he is currently seeing the forgot password form
       */
      $curStatus = "resetPasswordRequestForm";
    }elseif( isset($_GET['resetPassToken']) && !isset($_POST['logSysForgotPassChange']) ){
      /**
       * The user gave the password reset token. Check if the token is valid.
       */
      $reset_pass_token = urldecode($_GET['resetPassToken']);
      $sql = $this->dbh->prepare("SELECT COUNT(1) FROM ". $this->config['db']['token_table'] ." WHERE token = ?");
      $sql->execute(array($reset_pass_token));

      if($sql->fetchColumn() == 0 || $reset_pass_token == ""){
        $curStatus = "invalidToken"; // The token user gave was not valid
        echo $this->getOutput($curStatus);
      }else{
        /**
         * The token is valid, display the new password form
         */
        echo $this->getOutput("resetPasswordForm", array(
          "resetPassToken" => $reset_pass_token
        ));

        /**
         * The token was correct, displayed the change/new password form
         */
        $curStatus = "resetPasswordForm";
      }
    }elseif(isset($_POST['logSysForgotPassChange']) && isset($_POST['logSysForgotPassNewPassword']) && isset($_POST['logSysForgotPassRetypedPassword'])){
      $reset_pass_token = urldecode($_POST['token']);
      $sql = $this->dbh->prepare("SELECT uid FROM ". $this->config['db']['token_table'] ." WHERE token = ?");
      $sql->execute(array($reset_pass_token));

      $userID = $sql->fetchColumn();

      if( $userID == null || $reset_pass_token == null ){
        $curStatus = "invalidToken"; // The token user gave was not valid
        echo $this->getOutput($curStatus);
      }else{
        if($_POST['logSysForgotPassNewPassword'] == "" || $_POST['logSysForgotPassRetypedPassword'] == ""){
          $curStatus = "fieldsLeftBlank";
          echo $this->getOutput($curStatus);
        }elseif( $_POST['logSysForgotPassNewPassword'] != $_POST['logSysForgotPassRetypedPassword'] ){
          $curStatus = "passwordDontMatch"; // The new password and retype password submitted didn't match
          echo $this->getOutput($curStatus);
        }else{
          $this->changePassword($_POST['logSysForgotPassNewPassword'], $userID);

          /**
           * The token shall not be used again, so remove it.
           */
          $sql = $this->dbh->prepare("DELETE FROM ". $this->config['db']['token_table'] ." WHERE token = ?");
          $sql->execute(array($reset_pass_token));

          $curStatus = "passwordChanged"; // The password was successfully changed
          echo $this->getOutput($curStatus);
        }
      }
    }elseif(isset($_POST['identification'])){
      /**
       * Check if username/email is provided and if it's valid and exists
       */
      $identification = $_POST['identification'];
      if($identification == ""){
        $curStatus = "identityNotProvided"; // The identity was not given
        echo $this->getOutput($curStatus, array(
          "identity_type" => $identName
        ));
      }else{
        $sql = $this->dbh->prepare("SELECT ". $this->config["db"]["columns"]["email"] .", ". $this->config["db"]["columns"]["id"] ." FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login OR ". $this->config["db"]["columns"]["email"] ."=:login");
        $sql->bindValue(":login", $identification);

        $sql->execute();
        $cols  = $sql->fetch(\PDO::FETCH_ASSOC);

        if(empty($cols)){
          $curStatus = "userNotFound"; // The user with the identity given was not found in the users database
          echo $this->getOutput($curStatus);
        }else{
          $email = $cols['email'];
          $uid   = $cols['id'];

          /**
           * Make token and insert into the table
           */
          $token = self::rand_string(40);
          $sql = $this->dbh->prepare("INSERT INTO ". $this->config['db']['token_table'] ." (token, uid, requested) VALUES (?, ?, UNIX_TIMESTAMP())");
          $sql->execute(array($token, $uid));
          $encodedToken = urlencode($token);

          /**
           * Prepare the email to be sent
           */
          $subject = "Reset Password";
          $body   = "You requested for resetting your password on ". $this->config['basic']['company'] .". For this, please click the following link :
          <blockquote>
            <a href='". self::curPageURL() ."?resetPassToken={$encodedToken}'>Reset Password : {$token}</a>
          </blockquote>";
          $this->sendMail($email, $subject, $body);

          echo "<p>An email has been sent to your email inbox with instructions. Check Your Mail Inbox and SPAM Folders.</p><p>You can close this window.</p>";
          $curStatus = "emailSent"; // E-Mail has been sent
        }
      }
    }
    return $curStatus;
  }

  /**
   * A function that handles the logged in user to change her/his password
   *
   * @param  string $newPassword The new password
   * @return boolean             Whether operation was succesful
   */
  public function changePassword($newPassword, $userID = null){
    if($userID === null){
      $userID = $this->userID;
    }

    $hashedPassword = password_hash($newPassword . $this->config['keys']['salt'], PASSWORD_DEFAULT);
    $this->updateUser(array(
      "password" => $hashedPassword
    ), $userID);

    return true;
  }

  /**
   * Check if user exists
   * @param  string  $identification Username or email
   * @return boolean                 Whether user exist
   */
  public function userExists($identification){
    if($this->config['features']['email_login'] === true){
      $query = "SELECT COUNT(1) FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login OR ". $this->config["db"]["columns"]["email"] ."=:login";
    }else{
      $query = "SELECT COUNT(1) FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["username"] ."=:login";
    }
    $sql = $this->dbh->prepare($query);
    $sql->execute(array(
      ":login" => $identification
    ));
    return $sql->fetchColumn() == "0" ? false : true;
  }

  /**
   * Get user's info
   * @param  string       $what Column name
   * @param  string       $user User ID
   * @return array|string Value
   */
  public function getUser($what = "*", $user = null){
    if($user === null){
      $user = $this->userID;
    }

    if( is_array($what) ){
      $columns = implode(",", $what);
      $columns  = "{$columns}";
    }else{
      $columns = $what != "*" ? "$what" : "*";
    }

    $sql = $this->dbh->prepare("SELECT {$columns} FROM ". $this->config['db']['table'] ." WHERE ". $this->config["db"]["columns"]["id"] ." = ? ORDER BY ". $this->config["db"]["columns"]["id"] ." LIMIT 1");
    $sql->execute(array($user));

    $data = $sql->fetch(\PDO::FETCH_ASSOC);
    if( !is_array($what) ){
      $data = $what == "*" ? $data : $data[$what];
    }
    return $data;
  }

  /**
   * Updates the info of user
   * @param  array  $toUpdate Fields to update
   * @param  [type] $user     User ID
   * @return boolean          Whether it was a success
   */
  public function updateUser($toUpdate = array(), $user = null){
    if( is_array($toUpdate) && !isset($toUpdate['id']) ){
      
      if($user === null){
        $user = $this->userID;
      }

      $columns = "";
      foreach($toUpdate as $k => $v){
        $columns .= "$k = :$k, ";
      }
      $columns = substr($columns, 0, -2); // Remove last ","

      $sql = $this->dbh->prepare("UPDATE ". $this->config['db']['table'] ." SET {$columns} WHERE ". $this->config["db"]["columns"]["id"] ."=:id");
      $sql->bindValue(":id", $user);
      foreach($toUpdate as $key => $value){
        $value = htmlspecialchars($value);
        $sql->bindValue(":$key", $value);
      }
      $sql->execute();
      return true;
    }else{
      return false;
    }
  }



  /**
   * Get the time since user joined
   * @param  string $user User ID
   * @return string       Time since
   */
  public function joinedSince($user = null){
    if($user === null){
      $user = $this->userID;
    }

    $created = $this->getUser("created");
    $timeFirst  = strtotime($created);
    $timeSecond = strtotime("now");
    $memsince   = $timeSecond - strtotime($created);
    $regged     = date("n/j/Y", strtotime($created));

    if($memsince < 60) {
      $memfor = $memsince . " Seconds";
    }else if($memsince < 120){
      $memfor = floor ($memsince / 60) . " Minute";
    }else if($memsince < 3600 && $memsince > 120){
      $memfor = floor($memsince / 60) . " Minutes";
    }else if($memsince < 7200 && $memsince > 3600){
      $memfor = floor($memsince / 3600) . " Hour";
    }else if($memsince < 86400 && $memsince > 3600){
      $memfor = floor($memsince / 3600) . " Hours";
    }else if($memsince < 172800){
      $memfor = floor($memsince / 86400) . " Day";
    }else if($memsince < 604800 && $memsince > 172800){
      $memfor = floor($memsince / 86400) . " Days";
    }else if($memsince < 1209600 && $memsince > 604800){
      $memfor = floor($memsince / 604800) . " Week";
    }else if($memsince < 2419200 && $memsince > 1209600){
      $memfor = floor($memsince / 604800) . " Weeks";
    }else if($memsince < 4838400){
      $memfor = floor($memsince / 2419200) . " Month";
    }else if($memsince < 31536000 && $memsince > 4838400){
      $memfor = floor($memsince / 2419200) . " Months";
    }else if($memsince < 63072000){
      $memfor = floor($memsince / 31536000) . " Year";
    }else if($memsince > 63072000){
      $memfor = floor($memsince / 31536000) . " Years";
    }
    return (string) $memfor;
  }

  /**
   * 2 Step Verification Login Process
   * ---------------------------------
   * When user logs in, it checks whether there is a device cookie.
   * If there is :
   *   * Checks whether device is registered
   *   * If registered and username & password is correct, user is logged in
   * If there is not :
   *   * Token is sent
   *   * The "Enter Received token" form is shown
   *   * If the token entered is correct, then a device ID is inserted to table
   *   * The cookie with device ID is created
   * @see  LS::login()      Parameters are similar
   *
   * @param  string         $identification Similar to LS::login()
   * @param  string         $password       Similar to LS::login()
   * @param  boolean        $remember_me    Similar to LS::login()
   * @return boolean|string                 Whether login was successful
   *                                        Current state of 2 Step Login
   */
  public function twoStepLogin($identification = "", $password = "", $remember_me = false){
    if(isset($_POST['logSys_two_step_login-token']) && isset($_POST['logSys_two_step_login-uid']) && $_SESSION['logSys_two_step_login-first_step'] === '1'){
      /**
       * The user's ID and token is got through the form
       * User = One who is about to log in and is stuck at 2 step verification
       */
      $uid = $_POST['logSys_two_step_login-uid'];
      $token = $_POST['logSys_two_step_login-token'];

      $sql = $this->dbh->prepare("SELECT COUNT(1) FROM ". $this->config['db']['token_table'] ." WHERE token = ? AND uid = ?");
      $sql->execute(array($token, $uid));

      if($sql->fetchColumn() == 0){
        /**
         * To prevent user from Brute Forcing the token, we set the
         * status of the first login step to false,
         * so that the user would have to login again
         */
        $_SESSION['logSys_two_step_login-first_step'] = '0';
        echo $this->getOutput("invalidToken");
        return "invalidToken";
      }else{
        /**
         * Register User's new device if and only if
         * the user wants to remember the device from
         * which the user is logging in
         */
        if(isset($_POST['logSys_two_step_login-dontask'])){
          $device_token = self::rand_string(10);
          $sql = $this->dbh->prepare("INSERT INTO ". $this->config['two_step_login']['devices_table'] ." (uid, token, last_access) VALUES (?, ?, NOW())");
          $sql->execute(array($uid, $device_token));
          setcookie($this->config["cookies"]["names"]["device"], $device_token, strtotime($this->config['two_step_login']['expire']), $this->config['cookies']['path'], $this->config['cookies']['domain']);
        }else{
          /**
           * Verify login for this session
           */
          $_SESSION[ $this->config['cookies']['names']['device_verified'] ] = '1';
        }

        /**
         * Revoke token from reusing
         */
        $sql = $this->dbh->prepare("DELETE FROM ". $this->config['db']['token_table'] ." WHERE token = ? AND uid = ?");
        $sql->execute(array($token, $uid));
        $this->login($this->getUser("username", $uid), false, isset($_POST['logSys_two_step_login-remember_me']));
      }
      return true;
    }else if($identification !== "" && $password !== ""){
      $login = $this->login($identification, $password, $remember_me, false);
      if($login === false){
        /**
         * Username/Password wrong
         */
        return false;
      }else if(is_array($login) && $login['status'] == "blocked"){
        return false;
      }
        else if(is_array($login) && $login['stat'] == "deactive"){
        if($this->initCalled){
            self::redirect($this->config['pages']['deactive_page']);
      }
       return $login;
        }
      else{
        /**
         * Get the user ID from \Fr\LS::login()
         */
        $uid = $login;

        /**
         * Check if device is verfied so that 2 Step Verification can be skipped
         */
        if(isset($_COOKIE[$this->config["cookies"]["names"]["device"]])){
          $sql = $this->dbh->prepare("SELECT 1 FROM ". $this->config['two_step_login']['devices_table'] ." WHERE uid = ? AND token = ?");
          $sql->execute(array($uid, $_COOKIE[$this->config["cookies"]["names"]["device"]]));
          if($sql->fetchColumn() == "1"){
            $verfied = true;
            /**
             * Update last accessed time
             */
            $sql = $this->dbh->prepare("UPDATE ". $this->config['two_step_login']['devices_table'] ." SET last_access = NOW() WHERE uid = ? AND token = ?");
            $sql->execute(array($uid, $_COOKIE[$this->config["cookies"]["names"]["device"]]));

            $this->login($this->getUser("username", $uid), false, $remember_me);
            return true;
          }
        }
        /**
         * Start the 2 Step Verification Process
         * Do only if callback is present and if
         * the device is not verified
         */
        if(is_callable($this->config['two_step_login']['send_callback']) && !isset($verified)){
          /**
           * The first part of 2 Step Login is completed
           */
          $_SESSION['logSys_two_step_login-first_step'] = '1';

          /**
           * The 2nd parameter depends on `config` -> `two_step_login` -> `numeric`
           */
          $token = self::rand_string($this->config['two_step_login']['token_length'], $this->config['two_step_login']['numeric']);

          /**
           * Save the token in DB
           */
          $sql = $this->dbh->prepare("INSERT INTO ". $this->config['db']['token_table'] ." (token, uid, requested) VALUES (?, ?, NOW())");
          $sql->execute(array($token, $uid));

          $that = $this;
          call_user_func_array($this->config['two_step_login']['send_callback'], array(&$that, $uid, $token));

          /**
           * Display the form
           */
          echo $this->getOutput("twoStepLoginVerifyForm", array(
            "remember_me" => $remember_me,
            "uid" => $uid
          ));
          return "formDisplay";
        }else{
          self::log("two_step_login: Token Callback not present");
        }
      }
    }
    /**
     * 2 Step Login is not doing any actions or
     * hasn't returned anything before. If so,
     * then return false to indicate that the
     * function is not doing anything
     */
    return false;
  }

  /**
   * Get authorized devices
   * @return array Authorized devices
   */
  public function getDevices(){
    if($this->loggedIn){
      $sql = $this->dbh->prepare("SELECT * FROM ". $this->config['two_step_login']['devices_table'] ." WHERE uid = ?");
      $sql->execute(array($this->userID));
      return $sql->fetchAll(\PDO::FETCH_ASSOC);
    }else{
      return false;
    }
  }

  /**
   * Revoke a device
   * @param  string  $device_token Device ID
   * @return boolean               Whether it was revoked
   */
  public function revokeDevice($device_token){
    if($this->loggedIn){
      $sql = $this->dbh->prepare("DELETE FROM ". $this->config['two_step_login']['devices_table'] ." WHERE uid = ? AND token = ?");
      $sql->execute(array($this->userID, $device_token));
      if(isset($_SESSION[ $this->config['cookies']['names']['device_verified'] ])){
        unset($_SESSION[ $this->config['cookies']['names']['device_verified'] ]);
      }
      return $sql->rowCount() == 1;
    }
  }

  /**
   * Get output for each states
   * @param  string $state     The output of the state
   * @param  array  $extraInfo Extra parameters about the state
   * @return string            HTML output
   */
  public function getOutput($state, $extraInfo = array()){
    if(is_callable($this->config["basic"]["output_callback"])){
      $that = $this;
      return call_user_func_array($this->config["basic"]["output_callback"], array(
        &$that,
        $state,
        $extraInfo
      ));
    }else{
      return null;
    }
  }

  /**
   * Whether a user is logged in
   * @return boolean
   */
  public function isLoggedIn(){
    return $this->loggedIn;
  }

  /**
   * ---------------------
   * Extra Tools/Functions
   * ---------------------
   */

  /**
   * Check if E-Mail is valid
   * @param  string $email Email
   * @return boolean
   */
  public static function validEmail($email = ""){
    return filter_var($email, FILTER_VALIDATE_EMAIL);
  }

  /**
   * Get the current page URL
   * @return string URL
   */
  public static function curPageURL() {
    $pageURL = 'http';
    if(isset($_SERVER["HTTPS"]) && $_SERVER["HTTPS"] == "on"){$pageURL .= "s";}
    $pageURL .= "://";
    if($_SERVER["SERVER_PORT"] != "80") {
      $pageURL .= $_SERVER["SERVER_NAME"].":".$_SERVER["SERVER_PORT"].$_SERVER["REQUEST_URI"];
    }else{
      $pageURL .= $_SERVER["SERVER_NAME"].$_SERVER["REQUEST_URI"];
    }
    return $pageURL;
  }

  /**
   * Generate a Random String
   * @param  int     $length Length of string
   * @param  boolean $int    Should string be integer
   * @return string          Random string
   */
  public static function rand_string($length, $int = false) {
    $random_str = "";
    $chars = $int ? "0516243741506927589" : "subinsblogabcdefghijklmanopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
    $size = strlen($chars) - 1;
    for($i = 0;$i < $length;$i++) {
      $random_str .= $chars[rand(0, $size)];
    }
    return $random_str;
  }

  /**
   * Get the current page path.
   * @return string Example: /mypage, /folder/mypage.php
   */
  public static function curPage(){
    $parts = parse_url(self::curPageURL());
    return $parts["path"];
  }

  /**
   * Do a redirect
   * @param  [type]  $url    Where to redirect to
   * @param  integer $status Redirect status code
   * @return void
   */
  public static function redirect($url, $status = 302){
    header("Location: $url", true, $status);
    exit;
  }

  /**
   * Send an email
   * @param  string $email   Recipient's email
   * @param  string $subject Subject
   * @param  string $body    Message
   * @return void
   */
  public function sendMail($email, $subject, $body){
    /**
     * If there is a callback for email sending, use it else PHP's mail()
     */
    if(is_callable($this->config['basic']['email_callback'])){
      $that = $this;
      call_user_func_array($this->config['basic']['email_callback'], array(&$that, $email, $subject, $body));
    }else{
      $headers = array();
      $headers[] = "MIME-Version: 1.0";
      $headers[] = "Content-type: text/html; charset=iso-8859-1";
      $headers[] = "From: ". $this->config['basic']['email'];
      $headers[] = "Reply-To: ". $this->config['basic']['email'];
      mail($email, $subject, $body, implode("\r\n", $headers));
    }
  }

  /**
   * CSRF Protection
   */
  public function csrf($type = ""){
    if(!isset($_COOKIE['csrf_token'])){
      $csrf_token = self::rand_string(5);
      setcookie("csrf_token", $csrf_token, 0, $this->config['cookies']['path'], $this->config['cookies']['domain']);
    }else{
      $csrf_token = $_COOKIE['csrf_token'];
    }
    if($type == "s"){
      /**
       * Output as string
       */
      return urlencode($csrf_token);
    }elseif($type == "g"){
      /**
       * Output as a GET parameter
       */
      return "&csrf_token=" . urlencode($csrf_token);
    }elseif($type == "i"){
      /**
       * Output as an input field
       */
      echo "<input type='hidden' name='csrf_token' value='{$csrf_token}' />";
    }else{
      /**
       * Check CSRF validity
       */
      if((isset($_POST['csrf_token']) && $_COOKIE['csrf_token'] == $_POST['csrf_token']) || (isset($_GET['csrf_token']) && $_COOKIE['csrf_token'] == $_GET['csrf_token'])){
        return true;
      }else{
        /**
         * CSRF Token doesn't match.
         */
        return false;
      }
    }
  }

  /**
   * -------------------------
   * End Extra Tools/Functions
   * -------------------------
   */
}
 
 
My config  file code is
 
<?php
/**
 * For Development Purposes
 */
ini_set("display_errors", "on");

require __DIR__ . "/src/LS.php";
$config = array(
  "basic" => array(
    "company" => "************",
    "email" => "8888888"
  ),
  "db" => array(
    "host" => "***************",
    "port" => 3306,
    "username" => "******************",
    "password" => "*******************",
    "name" => "*****************t",
    "table" => "users"
  ),
  "features" => array(
    "auto_init" => true,
    "two_step_login" => true
  ),
  /**
   * These are my localhost paths, change it to yours
   */
  "pages" => array(
    "no_login" => array(
      "/user/",
      "/user/reset.php",
      "/user/registercomember.php"
    ),
    "everyone" => array(
      "/user/status.php",
      "/tips/nse6.php"
    ),
    "login_page" => "/user/login.php",
    "home_page" => "/tips/nse3.php",
    
  ),
  "two_step_login" => array(
    "instruction" => "A token was sent to your E-Mail Address. Please see the mail in your inbox and paste the token found in the textbox below :",
    "send_callback" => function(&$LS, $userID, $token){
      $email = $LS->getUser("email", $userID);
      $LS->sendMail($email, "Verify Yourself", "Someone tried to login to your account. If it was you, then use the following token to complete logging in : <blockquote>". $token ."</blockquote>If it was not you, then ignore this email and please consider to change your account's password.");
    }
  )
);

$LS = new \Fr\LS($config);
Edited by requinix
tldr
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.