- Modernized authentication with the introduction of TenantProject
- Created a base mail template - Adapt and improve document API to usage of catalogs and processors - Adapt eveai_sync to new authentication mechanism and usage of catalogs and processors
This commit is contained in:
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
class EveAI_Chat_API {
|
||||
private $security;
|
||||
private $eveai_api_url;
|
||||
|
||||
public function __construct() {
|
||||
$this->security = new EveAI_Chat_Security();
|
||||
$this->eveai_api_url = 'https://api.eveai.com'; // Should come from settings
|
||||
}
|
||||
|
||||
public function register_routes() {
|
||||
register_rest_route('eveai/v1', '/session-token', array(
|
||||
'methods' => 'POST',
|
||||
'callback' => array($this, 'get_session_token'),
|
||||
'permission_callback' => array($this, 'verify_request'),
|
||||
'args' => array(
|
||||
'tenant_id' => array(
|
||||
'required' => true,
|
||||
'validate_callback' => function($param) {
|
||||
return is_numeric($param);
|
||||
}
|
||||
),
|
||||
'domain' => array(
|
||||
'required' => true,
|
||||
'validate_callback' => function($param) {
|
||||
return is_string($param) && !empty($param);
|
||||
}
|
||||
)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public function verify_request($request) {
|
||||
// Origin verification
|
||||
$origin = $request->get_header('origin');
|
||||
if (!$this->security->verify_origin($origin)) {
|
||||
return new WP_Error(
|
||||
'invalid_origin',
|
||||
'Invalid request origin',
|
||||
array('status' => 403)
|
||||
);
|
||||
}
|
||||
|
||||
// Nonce verification
|
||||
$nonce = $request->get_header('X-WP-Nonce');
|
||||
if (!wp_verify_nonce($nonce, 'wp_rest')) {
|
||||
return new WP_Error(
|
||||
'invalid_nonce',
|
||||
'Invalid nonce',
|
||||
array('status' => 403)
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function get_session_token($request) {
|
||||
try {
|
||||
// Get the API key from WordPress options and decrypt it
|
||||
$settings = get_option('eveai_chat_settings');
|
||||
$encrypted_api_key = $settings['api_key'] ?? '';
|
||||
|
||||
if (empty($encrypted_api_key)) {
|
||||
return new WP_Error(
|
||||
'no_api_key',
|
||||
'API key not configured',
|
||||
array('status' => 500)
|
||||
);
|
||||
}
|
||||
|
||||
$api_key = $this->security->decrypt_sensitive_data($encrypted_api_key);
|
||||
|
||||
// Get parameters from request
|
||||
$tenant_id = $request->get_param('tenant_id');
|
||||
$domain = $request->get_param('domain');
|
||||
|
||||
// Request a session token from EveAI server
|
||||
$response = wp_remote_post(
|
||||
$this->eveai_api_url . '/session',
|
||||
array(
|
||||
'headers' => array(
|
||||
'Authorization' => 'Bearer ' . $api_key,
|
||||
'Content-Type' => 'application/json'
|
||||
),
|
||||
'body' => json_encode(array(
|
||||
'tenant_id' => $tenant_id,
|
||||
'domain' => $domain,
|
||||
'origin' => get_site_url()
|
||||
)),
|
||||
'timeout' => 15,
|
||||
'data_format' => 'body'
|
||||
)
|
||||
);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
throw new Exception($response->get_error_message());
|
||||
}
|
||||
|
||||
$response_code = wp_remote_retrieve_response_code($response);
|
||||
if ($response_code !== 200) {
|
||||
throw new Exception('Invalid response from EveAI server: ' . $response_code);
|
||||
}
|
||||
|
||||
$body = json_decode(wp_remote_retrieve_body($response), true);
|
||||
|
||||
if (empty($body['token'])) {
|
||||
throw new Exception('No token received from EveAI server');
|
||||
}
|
||||
|
||||
// Log the token generation (optional, for debugging)
|
||||
error_log(sprintf(
|
||||
'Generated session token for tenant %d from domain %s',
|
||||
$tenant_id,
|
||||
$domain
|
||||
));
|
||||
|
||||
return array(
|
||||
'success' => true,
|
||||
'session_token' => $body['token']
|
||||
);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('EveAI session token generation failed: ' . $e->getMessage());
|
||||
|
||||
return new WP_Error(
|
||||
'token_generation_failed',
|
||||
'Failed to generate session token: ' . $e->getMessage(),
|
||||
array('status' => 500)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the session token with EveAI server
|
||||
* Can be used for additional security checks
|
||||
*/
|
||||
public function validate_session_token($token) {
|
||||
try {
|
||||
$response = wp_remote_post(
|
||||
$this->eveai_api_url . '/validate-token',
|
||||
array(
|
||||
'headers' => array(
|
||||
'Content-Type' => 'application/json'
|
||||
),
|
||||
'body' => json_encode(array(
|
||||
'token' => $token
|
||||
)),
|
||||
'timeout' => 15
|
||||
)
|
||||
);
|
||||
|
||||
if (is_wp_error($response)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$body = json_decode(wp_remote_retrieve_body($response), true);
|
||||
return isset($body['valid']) && $body['valid'] === true;
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log('Token validation failed: ' . $e->getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
class EveAI_Chat_Loader {
|
||||
private $version;
|
||||
|
||||
public function __construct() {
|
||||
$this->version = EVEAI_CHAT_VERSION;
|
||||
$this->load_dependencies();
|
||||
}
|
||||
|
||||
private function load_dependencies() {
|
||||
// Load required files
|
||||
require_once EVEAI_CHAT_PLUGIN_DIR . 'includes/class-eveai-api.php';
|
||||
require_once EVEAI_CHAT_PLUGIN_DIR . 'includes/class-eveai-security.php';
|
||||
|
||||
// Load admin if in admin area
|
||||
if (is_admin()) {
|
||||
require_once EVEAI_CHAT_PLUGIN_DIR . 'admin/class-eveai-admin.php';
|
||||
}
|
||||
}
|
||||
|
||||
public function run() {
|
||||
// Initialize components
|
||||
$this->define_admin_hooks();
|
||||
$this->define_public_hooks();
|
||||
$this->define_shortcodes();
|
||||
}
|
||||
|
||||
private function define_admin_hooks() {
|
||||
if (is_admin()) {
|
||||
$admin = new EveAI_Chat_Admin($this->version);
|
||||
add_action('admin_menu', array($admin, 'add_plugin_admin_menu'));
|
||||
add_action('admin_init', array($admin, 'register_settings'));
|
||||
}
|
||||
}
|
||||
|
||||
private function define_public_hooks() {
|
||||
// Enqueue scripts and styles
|
||||
add_action('wp_enqueue_scripts', array($this, 'enqueue_assets'));
|
||||
|
||||
// Register REST API endpoints
|
||||
add_action('rest_api_init', array($this, 'register_rest_routes'));
|
||||
}
|
||||
|
||||
private function define_shortcodes() {
|
||||
add_shortcode('eveai_chat', array($this, 'render_chat_widget'));
|
||||
}
|
||||
|
||||
public function enqueue_assets() {
|
||||
// Enqueue required scripts
|
||||
wp_enqueue_script('socket-io', 'https://cdn.socket.io/4.0.1/socket.io.min.js', array(), '4.0.1', true);
|
||||
wp_enqueue_script('marked', 'https://cdn.jsdelivr.net/npm/marked/marked.min.js', array(), '1.0.0', true);
|
||||
|
||||
// Enqueue our scripts
|
||||
wp_enqueue_script(
|
||||
'eveai-sdk',
|
||||
EVEAI_CHAT_PLUGIN_URL . 'public/js/eveai-sdk.js',
|
||||
array('socket-io', 'marked'),
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
wp_enqueue_script(
|
||||
'eveai-chat-widget',
|
||||
EVEAI_CHAT_PLUGIN_URL . 'public/js/eveai-chat-widget.js',
|
||||
array('eveai-sdk'),
|
||||
$this->version,
|
||||
true
|
||||
);
|
||||
|
||||
// Enqueue styles
|
||||
wp_enqueue_style('material-icons', 'https://fonts.googleapis.com/icon?family=Material+Icons');
|
||||
wp_enqueue_style(
|
||||
'eveai-chat-style',
|
||||
EVEAI_CHAT_PLUGIN_URL . 'public/css/eveai-chat-style.css',
|
||||
array(),
|
||||
$this->version
|
||||
);
|
||||
|
||||
// Add WordPress-specific configuration
|
||||
wp_localize_script('eveai-sdk', 'eveaiWP', array(
|
||||
'nonce' => wp_create_nonce('wp_rest'),
|
||||
'ajaxUrl' => admin_url('admin-ajax.php'),
|
||||
'restUrl' => rest_url('eveai/v1/')
|
||||
));
|
||||
}
|
||||
|
||||
public function register_rest_routes() {
|
||||
$api = new EveAI_Chat_API();
|
||||
$api->register_routes();
|
||||
}
|
||||
|
||||
public function render_chat_widget($atts) {
|
||||
$defaults = array(
|
||||
'tenant_id' => '',
|
||||
'language' => 'en',
|
||||
'supported_languages' => 'en,fr,de,es',
|
||||
'server_url' => 'https://evie.askeveai.com',
|
||||
'specialist_id' => '1'
|
||||
);
|
||||
|
||||
$atts = shortcode_atts($defaults, $atts, 'eveai_chat');
|
||||
$chat_id = 'chat-container-' . uniqid();
|
||||
|
||||
return sprintf(
|
||||
'<div id="%s"></div>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const eveAI = new EveAI({
|
||||
tenantId: "%s",
|
||||
language: "%s",
|
||||
languages: "%s",
|
||||
serverUrl: "%s",
|
||||
specialistId: "%s",
|
||||
proxyUrl: "%s"
|
||||
});
|
||||
eveAI.initializeChat("%s");
|
||||
});
|
||||
</script>',
|
||||
$chat_id,
|
||||
esc_js($atts['tenant_id']),
|
||||
esc_js($atts['language']),
|
||||
esc_js($atts['supported_languages']),
|
||||
esc_js($atts['server_url']),
|
||||
esc_js($atts['specialist_id']),
|
||||
esc_js(rest_url('eveai/v1/session-token')),
|
||||
esc_js($chat_id)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
<?php
|
||||
class EveAI_Chat_Security {
|
||||
public function verify_request($request) {
|
||||
// Verify nonce
|
||||
$nonce = $request->get_header('X-WP-Nonce');
|
||||
if (!wp_verify_nonce($nonce, 'wp_rest')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Verify origin
|
||||
$origin = $request->get_header('origin');
|
||||
if (!$this->verify_origin($origin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function verify_origin($origin) {
|
||||
// Get the site URL
|
||||
$site_url = parse_url(get_site_url(), PHP_URL_HOST);
|
||||
$origin_host = parse_url($origin, PHP_URL_HOST);
|
||||
|
||||
// Check if origin matches site URL or is a subdomain
|
||||
return $origin_host === $site_url ||
|
||||
strpos($origin_host, '.' . $site_url) !== false;
|
||||
}
|
||||
|
||||
public function encrypt_sensitive_data($data) {
|
||||
if (empty($data)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$encryption_key = $this->get_encryption_key();
|
||||
$iv = openssl_random_pseudo_bytes(16);
|
||||
$encrypted = openssl_encrypt(
|
||||
$data,
|
||||
'AES-256-CBC',
|
||||
$encryption_key,
|
||||
0,
|
||||
$iv
|
||||
);
|
||||
|
||||
return base64_encode($iv . $encrypted);
|
||||
}
|
||||
|
||||
public function decrypt_sensitive_data($encrypted_data) {
|
||||
if (empty($encrypted_data)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
$encryption_key = $this->get_encryption_key();
|
||||
$data = base64_decode($encrypted_data);
|
||||
$iv = substr($data, 0, 16);
|
||||
$encrypted = substr($data, 16);
|
||||
|
||||
return openssl_decrypt(
|
||||
$encrypted,
|
||||
'AES-256-CBC',
|
||||
$encryption_key,
|
||||
0,
|
||||
$iv
|
||||
);
|
||||
}
|
||||
|
||||
private function get_encryption_key() {
|
||||
$key = get_option('eveai_chat_encryption_key');
|
||||
if (!$key) {
|
||||
$key = bin2hex(random_bytes(32));
|
||||
update_option('eveai_chat_encryption_key', $key);
|
||||
}
|
||||
return $key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a local temporary token for additional security
|
||||
*/
|
||||
public function generate_local_token($tenant_id, $domain) {
|
||||
$data = array(
|
||||
'tenant_id' => $tenant_id,
|
||||
'domain' => $domain,
|
||||
'timestamp' => time(),
|
||||
'site_url' => get_site_url()
|
||||
);
|
||||
|
||||
return $this->encrypt_sensitive_data(json_encode($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies if the domain is allowed for the given tenant
|
||||
*/
|
||||
public function verify_tenant_domain($tenant_id, $domain) {
|
||||
// This could be enhanced with a database check of allowed domains per tenant
|
||||
$allowed_domains = array(
|
||||
parse_url(get_site_url(), PHP_URL_HOST),
|
||||
'localhost',
|
||||
// Add other allowed domains as needed
|
||||
);
|
||||
|
||||
$domain_host = parse_url($domain, PHP_URL_HOST);
|
||||
return in_array($domain_host, $allowed_domains);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enhanced origin verification
|
||||
*/
|
||||
public function verify_origin($origin) {
|
||||
if (empty($origin)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the allowed origins
|
||||
$site_url = parse_url(get_site_url(), PHP_URL_HOST);
|
||||
$allowed_origins = array(
|
||||
$site_url,
|
||||
'www.' . $site_url,
|
||||
'localhost',
|
||||
// Add any additional allowed origins
|
||||
);
|
||||
|
||||
$origin_host = parse_url($origin, PHP_URL_HOST);
|
||||
|
||||
// Check if origin matches allowed origins or is a subdomain
|
||||
foreach ($allowed_origins as $allowed_origin) {
|
||||
if ($origin_host === $allowed_origin ||
|
||||
strpos($origin_host, '.' . $allowed_origin) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user