0
\$\begingroup\$

I have a working example of 2 methods:

  • Forgot password: A user inserts an account number and an email => An email is sent to the user right after it creates a token in the database.

  • Validate Reset password: A user opens a link via email, the link includes a token and an account number => if both variables are valid - return {status:true}.

I would like a review for my code, my code just feels weird in some parts:

  1. Something when calling the methods in the User class inside the vairables feels weird. but I do want to use that object, I'm not so sure what I should do there..
  2. When running the forgotPassword()/resetPassword() in the Login Contoller just feels weird to me. as if it could be written better.
  3. Not having try/catch and not getting any {status:false} in cases where some of the if statements don't apply.

This is the Login controller:

namespace MyApp\Controllers;

class Login extends Controller 
{
   public function forgotPassword()
    { 
        if (Input::exists()) {

            if (Token::check(Input::get('token'))) {

                $validation = Validation::check($_POST, array(
                            'account_number' => array(
                                'required' => true,
                                'min' => 3,
                                'max' => 30
                            ),
                            'email' => array(
                                'required' => true,
                                'email' => true
                            )
                ));

                if ($validation['passed']) {
                    $this->user->forgotPassword();
                    $this->user->userForgotPassword->set(Input::get('account_number'), Input::get('email'));
                    $this->user->userForgotPassword->apply();
                }

                General::toJson([ 'status' => true, 'msg' => 'An Email has been sent to your inbox. Please follow the steps and reset your password.' ]); 
            }
        } 
        else {
            General::toJson(['status' => false, 'msg' => 'Please enter your Account number and Email.' ]);
        }       
    }


    public function resetPasswordValidation()
    {
        if (Input::exists()) {

            if (Token::check(Input::get('token')) ) {

                $validation = Validation::check($_POST, array(
                            'account_number'  => array(
                                'required'  => true, 
                                'min'       => 3,
                                'max'       => 30                                
                            ),
                            'fptoken'       => array(
                                'required'  => true, 
                                'min'       => 60,
                                'max'       => 60,
                                'bcrypt'    => true      
                            )
                ));

                if ( $validation['passed'] ) {

                    $this->user->resetPassword();

                    if ($this->user->userResetPassword->validate(Input::get('account_number'), Input::get('fptoken')))
                        General::toJson(['status' => true]);
                }
        }
    }

This is the User class:

namespace MyApp\Models;

class User
{

    public $id;
    public $firstName;
    public $lastName;
    public $userName;
    public $email;
    public $lastLogin;
    public $salt;
    public $accountNumber;
    public $password; 
    public $ip;
    public $loginTimestamp;
    public $isLoggedIn;
    public $changePass;
    public $forgotPasswordToken; 
    public $forgotPasswordTimeStamp; 

    # @obj user Login data (a template for a login insert)
    public $userLogin;
    # @obj user profile information (fullname, email, last_login, profile picture, etc')
    public $userDetatils;
    # @obj user Authenticator object
    public $userAuthenticator;
    # @obj Handle user Sessions (Sets user sessions, Check if sessions are set, Check timeout, ect')
    public $userSessions;
    # @obj Handle user Updates 
    public $userUpdates;
    # @obj Handle user forgot password
    public $userForgotPassword;
    # @obj Handle user reset password
    public $userResetPassword;



    public function forgotPassword()
    {
        # Create systemUserDedatils obj
        if ( !$this->systemUserForgotPassword instanceof UserForgotPassword )
            $this->userForgotPassword = new \MyApp\Models\User\UserForgotPassword($this); 

        return $this->userForgotPassword;
    }



    public function resetPassword()
    {
        # Create userDedatils obj
        if ( !$this->userResetPassword instanceof UserResetPassword )
            $this->userResetPassword = new \MyApp\Models\User\UserResetPassword($this); 

        return $this->userResetPassword;
    }



}

UserForgotPassword Model:

<?php 
namespace MyApp\Models\User;

use MyApp\Models\Base\Database as Base;
use MyApp\Models\User; 
use MyApp\Models\Account; 
use MyApp\Core\Exception\Handler\ForgotPasswordException;

/**
 *
 *  User Forgot Password class
 *
 */
class UserForgotPassword extends Base
{

/*=================================
=            Variables            =
=================================*/

    private $Account; 
    private $User; 
    private $token;

/*===============================
=            Methods            =
===============================*/

    public function __construct(User $User)
    {
        parent::__construct();
        $this->User   = $User;
        $this->Account     = Account::getInstance();
    }


    /**
     *
     *  Set data to the User object 
     *  @param      $AccountNumber   Str     Account Name 
     *  @param      $email          Str     Email 
     *
     */
    public function set(String $AccountNumber,String $email)
    {
        $this->User->AccountNumber = $AccountNumber;
        $this->User->email        = $email;
    }


    /**
     *
     *  Apply Forgot Password 
     *  1. Check if all data is ready to generate a "forgot password" token.
     *  2. Updates the User row with new "forgot password" token and sets a timestamp. 
     *  3. Sends an email with a "reset password" link 
     *  
     */
    public function apply()
    {
        if ( $this->check() ) {

            # Insert data to dashboard_users
            $this->tableInserts();

            # Send email
            $this->sendResetEmail();
        }
        else {
            return false; 
        }        
    }


    /**
     *
     *  Check if data for password renewal is valid: 
     *  1. Check if data is set properly 
     *  2. Fetch Account from backofice
     *  3. Connect to the Account DB 
     *  4. Validate user email exist and set data to the object
     *  @return             Bool        True/False
     *
     */
    private function check()
    {
        try 
        {
            # Check that data is set properly to the user obj
            if ( !isset($this->User->AccountNumber) || !isset($this->User->email) )
                throw new ForgotPasswordException('ForgotPassword: Account Name or Email are missing');

            # Fetch Account 
            if (!$this->Account->getAccoundByNumber($this->User->AccountNumber)) 
                throw new ForgotPasswordException('ForgotPassword: Account "'. $this->User->AccountNumber .'" set doesn\'t exist or might be fake!');

            # Connect to Account 
            if (!$this->db->account_connect($this->Account->host, $this->Account->dbName))
                throw new ForgotPasswordException('ForgotPassword: Cannot connect to the database');

            # Get User object data by email
            if (!$this->User->getByEmail()) 
                throw new ForgotPasswordException('ForgotPassword: Email "'. $this->User->email .'" set doesn\'t exist or might be fake!');


            return true;
       } # Catch so the code wont break, but print errors in log: 
       catch (ForgotPasswordException $e) {

            # Do something to catch the errors:
            // 

            # Log the error and return false.
            $e->log($e);
            return false;
       }
    }


    /**
     *
     *  Handle inserts to the Users database row 
     *  1. Generates a token 
     *  2. Handles the counter column (future feature)
     *  3. Fetches current time stamp
     *  4. Inserts 'fotgot_password_token', 'fotgot_password_ts' & 'fotgot_password_counter' to row 
     *
     */
    private function tableInserts()
    {
        # Generate token 
        $this->token    = $this->generateToken();

        # Counter (check/append in database) 
        // Future Feature ! 

        # Current timestamp 
        $date           = time();
        $date           = strtotime('+1 day', $date);

        # Update system users row
        $this->db->row("UPDATE dashboard_users SET forgot_password_token = :forgotPasswordToken, forgot_password_ts = :forgotPasswordTimeStamp WHERE system_user_id = :UserId", array('forgotPasswordToken' => $this->token, 'forgotPasswordTimeStamp' => date("Y-m-d H:i:s", $date), 'UserId' => $this->User->id));
    }


    /**
     *
     *  Generate token - will use the User: 
     *  ID, Last Name, Email, Last Login, Account name, and a string manually set
     *  @return     Str     Hashed BCRYPT token
     *
     */
    private function generateToken()
    {
        $string = $this->User->id . $this->User->lastName . $this->User->email . $this->User->lastLogin . $this->User->AccountNumber . 'this user forgot his password';
        return \MyApp\Helpers\Hash::create($string, $this->User->salt);
    }


    /**
     *
     *  Send Reset Link (via email)
     *
     */
    private function sendResetEmail()
    {
        $this->User->resetPassword();
        return $this->User->UserResetPassword->sendResetEmail();
    }

}

UserResetPassword Model:

<?php 
namespace MyApp\Models\User;

use MyApp\Models\Base\Database as Base;
use MyApp\Models\User; 
use MyApp\Models\Account; 
use MyApp\Core\Config;
use MyApp\Core\Exception\Handler\ResetPasswordException;

/**
 *
 *  User Forgot Password class
 *
 */
class UserResetPassword extends Base
{

/*=================================
=            Variables            =
=================================*/

    private $Account; 
    private $User; 
    private $token;

/*===============================
=            Methods            =
===============================*/

    public function __construct(User $User)
    {
        parent::__construct();
        $this->User   = $User;
        $this->Account     = Account::getInstance();
    }


    /**
     *
     *  Validates Token
     *  @param      $AccountNumber       String      Account name
     *  @param      $token              String      Forgot password token
     *  @return                         Bool        True if:
     *                                              1. Account exists
     *                                              2. User exists
     *                                              3. User requested to reset his/her password
     *                                              4. User token didn't expire
     *
     */
    public function validate(String $AccountNumber, String $token)
    {
        try {

            # Fetch Account 
            if (!$this->Account->getAccountByName($AccountNumber)) 
                throw new ForgotPasswordException('Reset Passord: Account "'. $AccountNumber .'" set doesn\'t exist or might be fake!');

            # Connect to Account 
            if (!$this->db->Account_connect($this->Account->host, $this->Account->dbName))
                throw new ForgotPasswordException('Reset Passord: Cannot connect to the database. Host: ' . $this->Account->host . 'Account: ' . $this->Account->dbName);

            # Check if token exists & fetch SysteUser data
            if ( !$this->isToken($token) )
                throw new ForgotPasswordException('Reset Passord: Token expired or doesn\'t exist in the User table.');

            return true;
        }
        finally {
        }
    }


    /**
     *
     *  Check if token is valid 
     *  @param      $token   String     Forgot Password Token
     *  @return              Bool
     *
     */
    private function isToken(String $token) 
    {
        # Search for the token in the dashboard_users table
        $result = $this->db->row("SELECT system_user_id FROM dashboard_users WHERE forgot_password_token = :token AND forgot_password_ts > NOW()", array('token' => $token));

        if ($result) 
            return true;    
    }


    /**
     *
     *  Send Reset Link (via email)
     *
     */
    public function sendResetEmail()
    {
        $email          = new \MyApp\Helpers\Email();

        $email->addAddress($this->User->email);

        $email->Subject = 'Reset Password';
        $email->Body    = $this->generateEmailMessage();

        return $email->send();
    }


    /**
     *
     *  Generates an email message
     *
     */
    private function generateEmailMessage()
    {
        return html_entity_decode("Dear Account, <br/> You are getting this email because you requested to reset your password. <br/>Please follow <a href='" . Config::WEB_URL . "?account_number={$this->User->AccountNumber}&fptoken={$this->token}'>this link</a> to proceed. <br/><br/> <b>Myapp</b>");
    }
}

Please ignore the face that the code contains static methods. I am aware of this issue.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.