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

namespace 	CodePunch\Config\SAML;

use 		CodePunch\Base\Util as UTIL;
use 		CodePunch\Base\Text as TEXT;
use 		CodePunch\DB\Audit as AUDIT;
use 		Exception;

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

class Okta Extends SAMLBase {
	
	private $metadata = null;
	private $client_id;
	private $client_secret = null;
	private $redirect_uri;
	
	###########################################################################
	
	public function __construct($auth, $doload=true)
	{ 
		parent::__construct($auth);
		if($doload)
			$this->load();
	}
	
	###########################################################################
	
	private function load()
	{
		$settings = $this->getSettings();
		$this->client_id = $settings['client_id']; 
		$this->client_secret = $settings['client_secret']; 
		$this->redirect_uri = UTIL::get_root_url() . "saml.php";
		$oktadomain = $settings['okta_domain'];
		if(isset($settings['metadata_url']) && $settings['metadata_url'] != "") {
			$metadata_url = $settings['metadata_url'];
			if(!UTIL::ends_with($metadata_url, "/"))
				$metadata_url .= "/";
			$metadata_url .= '.well-known/oauth-authorization-server';
		}
		else
			$metadata_url = "{$oktadomain}/.well-known/oauth-authorization-server";
		$metadata_url = preg_replace('/\x00|<[^>]*>?/', '', $metadata_url);
		// Fetch the authorization server metadata which contains a few URLs
		// that we need later, such as the authorization and token endpoints
		$md = UTIL::curl_get_url($metadata_url);
		if($md['status'] == 200)
			$this->metadata = json_decode($md['result']);
		else {
			if(!function_exists('curl_version'))
				echo('Okta-OpenID integration requires cURL and php_curl was not found. Please install the cURL extension');
			else
				echo("Unable to get Okta Authorization Meta Data from $metadata_url");
			exit;
		}
	}
	
	###########################################################################
	
	public function login()
	{
		$_SESSION['state'] = bin2hex(random_bytes(5));
		$authorize_url = $this->metadata->authorization_endpoint.'?'.http_build_query([
			'response_type' => 'code',
			'client_id' => $this->client_id,
			'redirect_uri' => $this->redirect_uri,
			'state' => $_SESSION['state'],
			'scope' => 'openid',
		]);
		header("Location: $authorize_url");
		exit;
	}
	
	###########################################################################
	# Local Logout
	
	public function logout()
	{
		$settings = $this->getSettings();
		$oktadomain = $settings['okta_domain']; 
		$url = "{$oktadomain}/login/signout?fromURI=" . $this->redirect_uri;
		parent::logout();
		session_unset();
		header("Location: $url");
		exit;
	}
	
	###########################################################################
	
	public function ACS()
	{
		
	}
	
	###########################################################################
	
	public function SLS()
	{
		// Single Logout Service
		$settings = $this->getSettings();
		$oktadomain = $settings['okta_domain']; 
		$url = "{$oktadomain}/login/signout?fromURI=" . $this->redirect_uri;
		parent::logout();
		if($this->IsLoggedIn()) {
			
			exit;
		}
	}
	
	###########################################################################
	
	public function hasSettings()
	{
		$settings = $this->getSettings();
		if(isset($settings['client_id']) && isset($settings['client_secret']) && isset($settings['okta_domain'])) {
			if($settings['client_id'] != "" && $settings['client_secret'] != "" && $settings['okta_domain'] != "")
				return true;
		}
		return false;
	}
	
	###########################################################################
	
	public function process()
	{
		if(isset($_GET['code'])) {
			if($_SESSION['state'] != $_GET['state']) {
				throw new Exception(TEXT::get("saml_invalid_state"));
			}

			if(isset($_GET['error'])) {
				throw new Exception(sprintf(TEXT::get("format_saml_returned_error_S"), htmlspecialchars($_GET['error'])));
			}

			$response = UTIL::curl_post_url($this->metadata->token_endpoint, 10, false, false, [
				'grant_type' => 'authorization_code',
				'code' => $_GET['code'],
				'redirect_uri' => $this->redirect_uri,
				'client_id' => $this->client_id,
				'client_secret' => $this->client_secret,
				]);
			if($response['status'] == 200)
				$response = json_decode($response['result']);
			if(!isset($response->access_token)) {
				throw new Exception(TEXT::get("saml_error_token"));
			}
			$token = UTIL::curl_post_url($this->metadata->introspection_endpoint, 10, false, false, [
				'token' => $response->access_token,
				'client_id' => $this->client_id,
				'client_secret' => $this->client_secret,
				]);
			if($token['status'] == 200)
				$token = json_decode($token['result']);
			if($token->active == 1) {
				$samldata = array('idp'=>'Okta', 'user'=>$token->username);
				$user = $this->get_valid_user_name($token->username);
				$this->processUser($user, $samldata);
			}
		}
		else 
			$this->login();
	}
	
	###########################################################################
	
	public function getOptions()
	{
		return array (
			'client_id' => array('string', 'ID Provider', 'Client ID [Uses OIDC - OpenID Connect at Okta]'),
			'client_secret' => array('string', 'ID Provider', 'Client Secret'),
			'okta_domain' => array('string', 'ID Provider', 'Okta Domain [<b>Important</b>: Okta - OpenID integration requires PHP_cURL to be installed on the server.]'),
			'metadata_url' => array('string', 'ID Provider', 'Okta Authorization Issuer URI [If left empty, https://&lt;your_okta_domain/&gt; will be used by default as the URI and the metadata will be obtained from https://&lt;your_okta_domain/&gt/.well-known/oauth-authorization-server]'),
		);
	}
	
	###########################################################################
	
	public function getName()
	{
		return "Okta";
	}

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

	public function getSettings()
	{
		$setup = new \CodePunch\Config\Settings($this->getAuth());
		$settingsInfo = array();
		$settingsInfo['client_id'] = $setup->getEncryptedOption("okta_client_id", "");
		$settingsInfo['client_secret'] = $setup->getEncryptedOption("okta_client_secret", "");
		$settingsInfo['okta_domain'] = $setup->getEncryptedOption("okta_okta_domain", "");
		$settingsInfo['metadata_url'] = $setup->getEncryptedOption("okta_metadata_url", "");
		if(strtolower(substr($settingsInfo['okta_domain'], 0, 8)) != "https://")
			$settingsInfo['okta_domain'] = "https://" . $settingsInfo['okta_domain'];
		
		return $settingsInfo;
	}
	
}

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