/**
 * GAuthxHook
 *
 * DESCRIPTION
 *
 * This Snippet is to be useds as a PRE-Hook for  Shaun McCormick's snippet "Login" - it allows for you to add
 * 2 factor authentication (using google authenticator) to your member only areas in any front-end context
 * on your site
 *
 * PROPERTIES:
 *
 * &tolerance - integer - optional - Default: 2 - Sets the amount of time / tolerance you wish the authenticator to allow a provided token to be valid for
 * &historyLimit - integer - optional - Default: 10 - Sets the number of provided tokens you wish to be remembered before a token could be used again
 *
 * USAGE:
 *
 * [[!Login? &loginTpl=`GAuthxLoginTpl` &preHooks=`GAuthxHook` &tolerance=`3` &historyLimit=`5`]]
 *
 */
//Load GAuthx Package
$path = MODX_CORE_PATH . 'components/gauthx/';$result = $modx->addPackage('gauthx',$path . 'model/','gauthx_');

$tolerance = $modx->getOption('tolerance', $scriptProperties, 2);
$historyLimit = $modx->getOption('historyLimit', $scriptProperties, 10);


// Get Form Fields
$formFields = $hook->getValues(); 
$token = $formFields['token'];
$username = $formFields['username'];
$password = $formFields['password'];

// If user attempting to logout return true to allow logout
if ($_GET['service'] == 'logout') return true;

// If user has input a username proceed, if not return error
if ($formFields['username']) {

    // Get user profile
    $user = $modx->getObject('modUser', array('username' => $formFields['username']));
    if (!$user) {

        // if the username entered doesn't exisit return error
        $errorMsg = 'Неправильный Email или пароль. Пожалуйста, проверьте введённые данные и попытайтесь снова.';
        $hook->addError('user',$errorMsg);
        return false;
    }
    
    // Get user id & profile
    $id = $user->get('id'); $profile = $user->getOne('Profile'); $userKeyCheck = $profile->get('extended'); 
    
    // Check if user is configured to use 2-Factor Auth
    $userKey = $userKeyCheck['userKey'];
    $twoFa = $userKeyCheck['2fa'];
    if ($twoFa == 'email'){
        
        $modx->setPlaceholders(array(
           'username' => $username,
           'password' => $password,
        ),'my.');
        $modx->log(1,$username);
         $userToken = $userKeyCheck['email_token'];
         
         if (!$userToken) {
                $bytes = openssl_random_pseudo_bytes(4);
                $userToken = bin2hex($bytes);
                
                $modx->getService('mail', 'mail.modPHPMailer');
    			$modx->mail->set(modMail::MAIL_FROM, $modx->getOption('emailsender'));
    			$modx->mail->set(modMail::MAIL_FROM_NAME, $modx->getOption('site_name'));
    				
    			$modx->mail->address('to', $profile->get('email'));
    		
    			/*Заголовок сообщения*/
    			$modx->mail->set(modMail::MAIL_SUBJECT, 'Токен двухфакторной авторизации');
    			$modx->mail->set(modMail::MAIL_BODY, 'Ваш токен - '.$userToken);
    			$modx->mail->setHTML(true);
    			if (!$modx->mail->send()) {
    				 $modx->log(modX::LOG_LEVEL_ERROR,'An error occurred while trying to send the email: '.$modx->mail->mailer->ErrorInfo);
    				 $errorMsg = 'Не удалось отправить токен на ваш email';
                     $hook->addError('user',$errorMsg);
                     return false;
    			}
    			$modx->mail->reset();
    			$userKeyCheck['email_token'] = $userToken;
    			$userKeyCheck['email_reset'] = 0;
    			$profile->set('extended',$userKeyCheck);
    			$profile->save();
                $errorMsg = 'Токен выслан на ваш email';
                $hook->addError('user',$errorMsg);
                return false;
         }else{
             
             if ($token == $userToken) {
                 
                    $userKeyCheck['email_token'] = '';
                    $userKeyCheck['email_reset'] = 0;
    			    $profile->set('extended',$userKeyCheck);
    			    $profile->save();
                    return true;
                    
                } else {
                 
    			
    			$userKeyCheck['email_reset'] += 1;
    			$profile->set('extended',$userKeyCheck);
    			$profile->save();
    			if ( $userKeyCheck['email_reset'] > 3  ){
    			        $bytes = openssl_random_pseudo_bytes(4);
                        $userToken = bin2hex($bytes);
                        
                        $modx->getService('mail', 'mail.modPHPMailer');
            			$modx->mail->set(modMail::MAIL_FROM, $modx->getOption('emailsender'));
            			$modx->mail->set(modMail::MAIL_FROM_NAME, $modx->getOption('site_name'));
            				
            		
            		    $modx->mail->address('to', $profile->get('email'));
    		
            			/*Заголовок сообщения*/
            			$modx->mail->set(modMail::MAIL_SUBJECT, 'Токен двухфакторной авторизации');
            			$modx->mail->set(modMail::MAIL_BODY, 'Ваш токен - '.$userToken);
            			$modx->mail->setHTML(true);
            			if (!$modx->mail->send()) {
            				 $modx->log(modX::LOG_LEVEL_ERROR,'An error occurred while trying to send the email: '.$modx->mail->mailer->ErrorInfo);
            				 $errorMsg = 'Не удалось отправить токен на ваш email';
                             $hook->addError('user',$errorMsg);
                             return false;
            			}
            			$modx->mail->reset();
            			$userKeyCheck['email_token'] = $userToken;
            			$profile->set('extended',$userKeyCheck);
            			$profile->save();
            			$errorMsg = 'Токен выслан повторно, проверьте email';
                        $hook->addError('user',$errorMsg);
                        return false;
    			    
    			}
                    $errorMsg = 'Неверный токен, попробуйте еще раз';
                    $hook->addError('user',$errorMsg);
                    return false;
                }
             
         }
             
         
        
        
    }elseif($twoFa == 'google'){
        if ($userKey) {

            // If the user has a userKey setup continue - then check if the provided token has been used by this user previously
            $submitHistory = $modx->getObject('UserHistory',array('internalKey' => $id,'previousKey' => $token));
            if ($submitHistory) {$SHC = $submitHistory->get('id');} else {$SHC = 0;}
    
            if ($SHC == 0) {
                // if user has not used the token before - continue - else return error
                
                require_once 'core/components/gauthx/elements/plugins/GoogleAuthenticator.php';
                $ga = new GoogleAuthenticator();
                
                // validate if code matches user token
                $checkResult = $ga->verifyCode($userKey, $token, $tolerance);    // 2 = 2*30sec clock tolerance
                
                    // if yes add provided token to user history, clean up user history & allow user to login. - Else return error
                if ($checkResult) {
    
                        
                    $log = $modx->newObject('UserHistory');
                        $log->set('internalKey', $id);
                        $log->set('previousKey', $token);
                    $log->save();
                    
                    // get count of user hisotry for user who is logging in
                    $max = $modx->getCount('UserHistory',array('internalKey' => $id));
                  
                    if ($max > $historyLimit) {
    
                        // if count is greater than history limit remove a row ()
                        
                        $newlimit = $max - $historyLimit;
                       
                        $query = $modx->newQuery('UserHistory'); 
                        $query->sortby('id','ASC');
                        $query->limit($newlimit);
    
                        $ls = $modx->getCollection('UserHistory',$query);
                        
                            foreach ($ls as $l) {
    
                               $l->remove();
                            }
                    }
                   
                    return true;
                } else {
    
                    $errorMsg = 'Неверный токен, попробуйте еще раз';
                    $hook->addError('user',$errorMsg);
                    return false;
                }
            }
            else {
    
                $errorMsg = 'Неверный токен, попробуйте еще раз';
                $hook->addError('user',$errorMsg);
                return false;
            }
            
        }
    }
    else {

        return true;
    }
}
else {

    $errorMsg ='Неправильный Email или пароль. Пожалуйста, проверьте введённые данные и попытайтесь снова.';
    $hook->addError('user',$errorMsg);
    return false;
}