<?php
###############################################################################
# DuoUniversal.php
#
# @author Anil Kumar <akumar@codepunch.com>
# @link   https://codepunch.com
#
############################################################################### 

namespace 	CodePunch\Config\Security;

use 		CodePunch\Base\Util as UTIL;
use			CodePunch\Base\Text as TEXT;
use			CodePunch\DB\Audit  as AUDIT;
use 		Exception;
use 		ErrorException;
use 		Duo\DuoUniversal\Client;
use 		Duo\DuoUniversal\DuoException;

###############################################################################

include_once(UTIL::get_install_folder_path()."thirdparty/2fa/duosecurity/duo_universal_php/src/Client.php");
include_once(UTIL::get_install_folder_path()."thirdparty/2fa/duosecurity/duo_universal_php/src/DuoException.php");

###############################################################################

class DuoUniversal extends Base {
	
	public function getKeys() {
		return array(
			'Integration Key' => 'Obtained from Duo', 
			'Secret Key' => 'Obtained from Duo', 
			'Host Name' => 'Obtained from Duo',
			'HTTP_Proxy' => 'If you have an HTTP proxy server eg: localhost:8081. Leave empty otherwise.'
		);
	}
	
	public function get() {
		$auth = $this->getAuthentication();
		$message = "";	
		$username = UTIL::get_from_array($_SESSION[\CodePunch\Config\Auth::USERNAME], "");
		if($username != "" && $auth && $auth->getDatabase()) {
			$db = $auth->getDatabase();
			$setup = new \CodePunch\Config\Settings($auth);
			
			$twofa_cookie_set_days = $setup->getString("twofa_cookie_set_days", 0);
			if($twofa_cookie_set_days <= 0 || $twofa_cookie_set_days > 45)
				$twofa_cookie_set_days = 0;
				
			$duo_integration_key = $setup->getEncryptedOption("duouniversal_key_integration_key", "");
			$duo_secret_key = $setup->getEncryptedOption("duouniversal_key_secret_key", "");
			$duo_hostname = $setup->getEncryptedOption("duouniversal_key_host_name", "");
			$duo_http_proxy = $setup->getEncryptedOption("duouniversal_key_http_proxy", "");
			$duo_http_proxy = strlen($duo_http_proxy) < 2 ? "" : $duo_http_proxy;
			$duo_failmode = "CLOSED";
			try {
				$duo_client = new Client(
					$duo_integration_key,
					$duo_secret_key,
					$duo_hostname,
					str_replace("http://", "https://", UTIL::get_root_url()) . "login.php",
					true,
					$duo_http_proxy
				);
			}
			catch (DuoException $e) {
				$logger = new \CodePunch\Base\CPLogger();
				$logger->error($e->getMessage());
				UTIL::print(get_class($this) . ": " . $e->getMessage());
				exit();
			}
			
			if (isset($_REQUEST['state']) && isset($_REQUEST['duo_code'])) {
				$state = UTIL::get_from_array($_SESSION['DUO_UNIVERSAL_STATE'], "");
				if($_REQUEST['state'] == $state) {
					try {
						$decoded_token = $duo_client->exchangeAuthorizationCodeFor2FAResult($_REQUEST['duo_code'], $username);
						$auth->processSecondaryLogin($username, $twofa_cookie_set_days);
					}
					catch (DuoException $e) {
						$logger = new \CodePunch\Base\CPLogger();
						$logger->error($e->getMessage());
						UTIL::print($e->getMessage());
						exit();
					}
				}
			}
			else {
				try {
					$duo_client->healthCheck();
					$state = $duo_client->generateState();
					$_SESSION['DUO_UNIVERSAL_STATE'] = $state;
					$prompt_uri = $duo_client->createAuthUrl($username, $state);
					header("Location: " . $prompt_uri);
					exit();
				} 
				catch (DuoException $e) {
					$logger = new \CodePunch\Base\CPLogger();
					$logger->error($e->getMessage());
					if ($duo_failmode == "OPEN")
						$logger->error("Login 'Successful', but 2FA Not Performed. Confirm Duo Universal client/secret/host values are correct");
					else
						$logger->error("2FA Unavailable. Confirm Duo Universal client/secret/host values are correct");
					UTIL::print(get_class($this) . ": " . $e->getMessage());
					exit();
				}
			}
		}
		
		$message = "Invalid primary login. There is no user information to trigger 2FA!";
		UTIL::print($message);
		exit();
	}
	
}


