chore: add spec-kit and standardize signatures
- Added GitHub spec-kit for development workflow - Standardized file signatures to Descomplicar® format - Updated development configuration 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
751
care-booking-block/includes/class-admin-interface.php
Normal file
751
care-booking-block/includes/class-admin-interface.php
Normal file
@@ -0,0 +1,751 @@
|
||||
/**
|
||||
* Descomplicar® Crescimento Digital
|
||||
* https://descomplicar.pt
|
||||
*/
|
||||
|
||||
<?php
|
||||
/**
|
||||
* Admin interface for Care Booking Block plugin
|
||||
*
|
||||
* @package CareBookingBlock
|
||||
*/
|
||||
|
||||
// Prevent direct access
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Admin interface class
|
||||
*/
|
||||
class Care_Booking_Admin_Interface
|
||||
{
|
||||
/**
|
||||
* Database handler instance
|
||||
*
|
||||
* @var Care_Booking_Database_Handler
|
||||
*/
|
||||
private $db_handler;
|
||||
|
||||
/**
|
||||
* Restriction model instance
|
||||
*
|
||||
* @var Care_Booking_Restriction_Model
|
||||
*/
|
||||
private $restriction_model;
|
||||
|
||||
/**
|
||||
* Admin page slug
|
||||
*/
|
||||
const ADMIN_PAGE_SLUG = 'care-booking-control';
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
*
|
||||
* @param Care_Booking_Database_Handler $db_handler Database handler instance
|
||||
*/
|
||||
public function __construct($db_handler)
|
||||
{
|
||||
$this->db_handler = $db_handler;
|
||||
$this->restriction_model = new Care_Booking_Restriction_Model();
|
||||
|
||||
$this->init_hooks();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize WordPress hooks
|
||||
*/
|
||||
private function init_hooks()
|
||||
{
|
||||
// Admin menu
|
||||
add_action('admin_menu', [$this, 'add_admin_menu']);
|
||||
|
||||
// Admin scripts and styles
|
||||
add_action('admin_enqueue_scripts', [$this, 'enqueue_admin_assets']);
|
||||
|
||||
// AJAX handlers
|
||||
add_action('wp_ajax_care_booking_get_restrictions', [$this, 'ajax_get_restrictions']);
|
||||
add_action('wp_ajax_care_booking_toggle_restriction', [$this, 'ajax_toggle_restriction']);
|
||||
add_action('wp_ajax_care_booking_bulk_update', [$this, 'ajax_bulk_update']);
|
||||
add_action('wp_ajax_care_booking_get_entities', [$this, 'ajax_get_entities']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add admin menu
|
||||
*/
|
||||
public function add_admin_menu()
|
||||
{
|
||||
add_management_page(
|
||||
__('Care Booking Control', 'care-booking-block'),
|
||||
__('Care Booking Control', 'care-booking-block'),
|
||||
'manage_options',
|
||||
self::ADMIN_PAGE_SLUG,
|
||||
[$this, 'render_admin_page']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Enqueue admin assets
|
||||
*
|
||||
* @param string $hook_suffix Current admin page
|
||||
*/
|
||||
public function enqueue_admin_assets($hook_suffix)
|
||||
{
|
||||
// Only load on our admin page
|
||||
if (strpos($hook_suffix, self::ADMIN_PAGE_SLUG) === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Enqueue CSS
|
||||
wp_enqueue_style(
|
||||
'care-booking-admin',
|
||||
CARE_BOOKING_BLOCK_PLUGIN_URL . 'admin/css/admin-style.css',
|
||||
[],
|
||||
CARE_BOOKING_BLOCK_VERSION
|
||||
);
|
||||
|
||||
// Enqueue JavaScript
|
||||
wp_enqueue_script(
|
||||
'care-booking-admin',
|
||||
CARE_BOOKING_BLOCK_PLUGIN_URL . 'admin/js/admin-script.js',
|
||||
['jquery'],
|
||||
CARE_BOOKING_BLOCK_VERSION,
|
||||
true
|
||||
);
|
||||
|
||||
// Localize script
|
||||
wp_localize_script('care-booking-admin', 'careBookingAjax', [
|
||||
'ajaxurl' => admin_url('admin-ajax.php'),
|
||||
'nonce' => wp_create_nonce('care_booking_nonce'),
|
||||
'strings' => [
|
||||
'loading' => __('Loading...', 'care-booking-block'),
|
||||
'error' => __('An error occurred. Please try again.', 'care-booking-block'),
|
||||
'confirm_bulk' => __('Are you sure you want to update all selected restrictions?', 'care-booking-block'),
|
||||
'success_update' => __('Restriction updated successfully.', 'care-booking-block'),
|
||||
'success_bulk' => __('Bulk update completed.', 'care-booking-block')
|
||||
]
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render admin page
|
||||
*/
|
||||
public function render_admin_page()
|
||||
{
|
||||
// Check KiviCare availability
|
||||
if (!$this->is_kivicare_active()) {
|
||||
$this->render_kivicare_warning();
|
||||
return;
|
||||
}
|
||||
|
||||
include CARE_BOOKING_BLOCK_PLUGIN_DIR . 'admin/partials/admin-display.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler: Get restrictions
|
||||
*/
|
||||
public function ajax_get_restrictions()
|
||||
{
|
||||
// SECURITY: Enhanced CSRF protection with additional request validation
|
||||
if (!wp_verify_nonce($_POST['nonce'] ?? '', 'care_booking_nonce')) {
|
||||
wp_send_json_error(['message' => __('Security check failed', 'care-booking-block')]);
|
||||
wp_die(); // Additional security measure
|
||||
}
|
||||
|
||||
// SECURITY: Check if request is actually via AJAX
|
||||
if (!wp_doing_ajax()) {
|
||||
wp_send_json_error(['message' => __('Invalid request method', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Enhanced capability check with logging
|
||||
if (!current_user_can('manage_options')) {
|
||||
error_log('Care Booking Block: Unauthorized access attempt from user ID: ' . get_current_user_id());
|
||||
wp_send_json_error(['message' => __('Insufficient permissions', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Rate limiting check
|
||||
if (!$this->check_rate_limit('get_restrictions')) {
|
||||
wp_send_json_error(['message' => __('Too many requests. Please wait.', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Enhanced input sanitization and validation
|
||||
$restriction_type = sanitize_text_field($_POST['restriction_type'] ?? 'all');
|
||||
$doctor_id = isset($_POST['doctor_id']) ? absint($_POST['doctor_id']) : null;
|
||||
|
||||
// SECURITY: Validate restriction_type against whitelist
|
||||
$allowed_types = ['all', 'doctor', 'service'];
|
||||
if (!in_array($restriction_type, $allowed_types, true)) {
|
||||
error_log('Care Booking Block: Invalid restriction_type attempted: ' . $restriction_type);
|
||||
wp_send_json_error(['message' => __('Invalid restriction type', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Validate doctor_id if provided
|
||||
if ($doctor_id !== null && $doctor_id <= 0) {
|
||||
wp_send_json_error(['message' => __('Invalid doctor ID', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
try {
|
||||
if ($restriction_type === 'all') {
|
||||
$restrictions = $this->restriction_model->get_all();
|
||||
} elseif (in_array($restriction_type, ['doctor', 'service'])) {
|
||||
$restrictions = $this->restriction_model->get_by_type($restriction_type);
|
||||
|
||||
// Filter by doctor if specified
|
||||
if ($restriction_type === 'service' && $doctor_id) {
|
||||
$restrictions = array_filter($restrictions, function($r) use ($doctor_id) {
|
||||
return $r->doctor_id == $doctor_id;
|
||||
});
|
||||
}
|
||||
} else {
|
||||
wp_send_json_error(['message' => __('Invalid parameters', 'care-booking-block')]);
|
||||
}
|
||||
|
||||
// SECURITY: Convert to array format with output escaping
|
||||
$formatted_restrictions = [];
|
||||
foreach ($restrictions as $restriction) {
|
||||
$formatted_restrictions[] = [
|
||||
'id' => (int) $restriction->id,
|
||||
'restriction_type' => esc_html($restriction->restriction_type),
|
||||
'target_id' => (int) $restriction->target_id,
|
||||
'doctor_id' => $restriction->doctor_id ? (int) $restriction->doctor_id : null,
|
||||
'is_blocked' => (bool) $restriction->is_blocked,
|
||||
'created_at' => esc_html($restriction->created_at),
|
||||
'updated_at' => esc_html($restriction->updated_at)
|
||||
];
|
||||
}
|
||||
|
||||
wp_send_json_success([
|
||||
'restrictions' => $formatted_restrictions,
|
||||
'total' => count($formatted_restrictions)
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
wp_send_json_error(['message' => __('Database error occurred', 'care-booking-block')]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler: Toggle restriction
|
||||
*/
|
||||
public function ajax_toggle_restriction()
|
||||
{
|
||||
// SECURITY: Enhanced CSRF protection
|
||||
if (!wp_verify_nonce($_POST['nonce'] ?? '', 'care_booking_nonce')) {
|
||||
wp_send_json_error(['message' => __('Security check failed', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: AJAX request validation
|
||||
if (!wp_doing_ajax()) {
|
||||
wp_send_json_error(['message' => __('Invalid request method', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Enhanced capability check with logging
|
||||
if (!current_user_can('manage_options')) {
|
||||
error_log('Care Booking Block: Unauthorized toggle attempt from user ID: ' . get_current_user_id());
|
||||
wp_send_json_error(['message' => __('Insufficient permissions', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Rate limiting
|
||||
if (!$this->check_rate_limit('toggle_restriction')) {
|
||||
wp_send_json_error(['message' => __('Too many requests. Please wait.', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Enhanced parameter validation and sanitization
|
||||
$restriction_type = sanitize_text_field($_POST['restriction_type'] ?? '');
|
||||
$target_id = absint($_POST['target_id'] ?? 0);
|
||||
$doctor_id = isset($_POST['doctor_id']) ? absint($_POST['doctor_id']) : null;
|
||||
$is_blocked = isset($_POST['is_blocked']) ? (bool) $_POST['is_blocked'] : true;
|
||||
|
||||
// SECURITY: Validate required parameters
|
||||
if (!$restriction_type || !$target_id) {
|
||||
error_log('Care Booking Block: Missing parameters in toggle_restriction');
|
||||
wp_send_json_error(['message' => __('Missing required parameters', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Whitelist validation for restriction_type
|
||||
$allowed_types = ['doctor', 'service'];
|
||||
if (!in_array($restriction_type, $allowed_types, true)) {
|
||||
error_log('Care Booking Block: Invalid restriction_type in toggle: ' . $restriction_type);
|
||||
wp_send_json_error(['message' => __('Invalid restriction type', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Validate target_id range
|
||||
if ($target_id <= 0 || $target_id > PHP_INT_MAX) {
|
||||
wp_send_json_error(['message' => __('Invalid target ID', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Service restriction validation
|
||||
if ($restriction_type === 'service' && (!$doctor_id || $doctor_id <= 0)) {
|
||||
wp_send_json_error(['message' => __('Valid doctor_id required for service restrictions', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
try {
|
||||
// Validate target exists in KiviCare
|
||||
if (!$this->validate_kivicare_target($restriction_type, $target_id, $doctor_id)) {
|
||||
wp_send_json_error(['message' => __('Target not found', 'care-booking-block')]);
|
||||
}
|
||||
|
||||
// Toggle restriction
|
||||
$result = $this->restriction_model->toggle($restriction_type, $target_id, $doctor_id, $is_blocked);
|
||||
|
||||
if ($result) {
|
||||
// Get updated/created restriction
|
||||
$restriction = $this->restriction_model->find_existing($restriction_type, $target_id, $doctor_id);
|
||||
|
||||
if ($restriction) {
|
||||
wp_send_json_success([
|
||||
'message' => __('Restriction updated successfully', 'care-booking-block'),
|
||||
'restriction' => [
|
||||
'id' => (int) $restriction->id,
|
||||
'restriction_type' => esc_html($restriction->restriction_type),
|
||||
'target_id' => (int) $restriction->target_id,
|
||||
'doctor_id' => $restriction->doctor_id ? (int) $restriction->doctor_id : null,
|
||||
'is_blocked' => (bool) $restriction->is_blocked,
|
||||
'updated_at' => esc_html($restriction->updated_at)
|
||||
]
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
wp_send_json_error(['message' => __('Failed to update restriction', 'care-booking-block')]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
wp_send_json_error(['message' => __('Database error', 'care-booking-block')]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler: Bulk update
|
||||
*/
|
||||
public function ajax_bulk_update()
|
||||
{
|
||||
// SECURITY: Enhanced CSRF protection
|
||||
if (!wp_verify_nonce($_POST['nonce'] ?? '', 'care_booking_nonce')) {
|
||||
wp_send_json_error(['message' => __('Security check failed', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: AJAX request validation
|
||||
if (!wp_doing_ajax()) {
|
||||
wp_send_json_error(['message' => __('Invalid request method', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Enhanced capability check with logging
|
||||
if (!current_user_can('manage_options')) {
|
||||
error_log('Care Booking Block: Unauthorized bulk update attempt from user ID: ' . get_current_user_id());
|
||||
wp_send_json_error(['message' => __('Insufficient permissions', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Strict rate limiting for bulk operations
|
||||
if (!$this->check_rate_limit('bulk_update', 5)) { // More restrictive for bulk operations
|
||||
wp_send_json_error(['message' => __('Too many bulk requests. Please wait.', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Enhanced parameter validation
|
||||
if (!isset($_POST['restrictions'])) {
|
||||
error_log('Care Booking Block: Missing restrictions parameter in bulk update');
|
||||
wp_send_json_error(['message' => __('Missing restrictions parameter', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
$restrictions = $_POST['restrictions'];
|
||||
|
||||
// SECURITY: Type validation
|
||||
if (!is_array($restrictions)) {
|
||||
error_log('Care Booking Block: Invalid restrictions format in bulk update');
|
||||
wp_send_json_error(['message' => __('Invalid restrictions format', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Strict bulk size limits for security
|
||||
if (count($restrictions) > 50) { // Reduced from 100 for security
|
||||
error_log('Care Booking Block: Bulk size limit exceeded: ' . count($restrictions));
|
||||
wp_send_json_error(['message' => __('Bulk size limit exceeded (max 50)', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Validate each restriction item
|
||||
foreach ($restrictions as $index => $restriction) {
|
||||
if (!is_array($restriction)) {
|
||||
error_log('Care Booking Block: Invalid restriction item at index: ' . $index);
|
||||
wp_send_json_error(['message' => __('Invalid restriction data format', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// Sanitize each restriction
|
||||
$restrictions[$index] = [
|
||||
'restriction_type' => sanitize_text_field($restriction['restriction_type'] ?? ''),
|
||||
'target_id' => absint($restriction['target_id'] ?? 0),
|
||||
'doctor_id' => isset($restriction['doctor_id']) ? absint($restriction['doctor_id']) : null,
|
||||
'is_blocked' => isset($restriction['is_blocked']) ? (bool) $restriction['is_blocked'] : true
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$result = $this->restriction_model->bulk_toggle($restrictions);
|
||||
|
||||
if (empty($result['errors'])) {
|
||||
wp_send_json_success([
|
||||
'message' => __('Bulk update completed', 'care-booking-block'),
|
||||
'updated' => $result['updated'],
|
||||
'errors' => []
|
||||
]);
|
||||
} else {
|
||||
// Partial failure
|
||||
wp_send_json_error([
|
||||
'message' => __('Partial failure in bulk update', 'care-booking-block'),
|
||||
'updated' => $result['updated'],
|
||||
'errors' => $result['errors']
|
||||
]);
|
||||
}
|
||||
|
||||
} catch (Exception $e) {
|
||||
wp_send_json_error(['message' => __('Bulk update failed', 'care-booking-block')]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* AJAX handler: Get KiviCare entities
|
||||
*/
|
||||
public function ajax_get_entities()
|
||||
{
|
||||
// SECURITY: Enhanced CSRF protection
|
||||
if (!wp_verify_nonce($_POST['nonce'] ?? '', 'care_booking_nonce')) {
|
||||
wp_send_json_error(['message' => __('Security check failed', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: AJAX request validation
|
||||
if (!wp_doing_ajax()) {
|
||||
wp_send_json_error(['message' => __('Invalid request method', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Enhanced capability check with logging
|
||||
if (!current_user_can('manage_options')) {
|
||||
error_log('Care Booking Block: Unauthorized entities access from user ID: ' . get_current_user_id());
|
||||
wp_send_json_error(['message' => __('Insufficient permissions', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Rate limiting
|
||||
if (!$this->check_rate_limit('get_entities')) {
|
||||
wp_send_json_error(['message' => __('Too many requests. Please wait.', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Enhanced input validation
|
||||
$entity_type = sanitize_text_field($_POST['entity_type'] ?? '');
|
||||
$doctor_id = isset($_POST['doctor_id']) ? absint($_POST['doctor_id']) : null;
|
||||
|
||||
if (!$entity_type) {
|
||||
error_log('Care Booking Block: Missing entity_type parameter');
|
||||
wp_send_json_error(['message' => __('Missing entity_type parameter', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Whitelist validation for entity_type
|
||||
$allowed_entity_types = ['doctors', 'services'];
|
||||
if (!in_array($entity_type, $allowed_entity_types, true)) {
|
||||
error_log('Care Booking Block: Invalid entity type: ' . $entity_type);
|
||||
wp_send_json_error(['message' => __('Invalid entity type', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// SECURITY: Validate doctor_id if provided
|
||||
if ($doctor_id !== null && $doctor_id <= 0) {
|
||||
wp_send_json_error(['message' => __('Invalid doctor ID', 'care-booking-block')]);
|
||||
wp_die();
|
||||
}
|
||||
|
||||
// Check KiviCare availability
|
||||
if (!$this->is_kivicare_active()) {
|
||||
wp_send_json_error(['message' => __('KiviCare plugin not available', 'care-booking-block')]);
|
||||
}
|
||||
|
||||
try {
|
||||
if ($entity_type === 'doctors') {
|
||||
$entities = $this->get_kivicare_doctors();
|
||||
} else {
|
||||
$entities = $this->get_kivicare_services($doctor_id);
|
||||
}
|
||||
|
||||
wp_send_json_success([
|
||||
'entities' => $entities,
|
||||
'total' => count($entities)
|
||||
]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
wp_send_json_error(['message' => __('Database error occurred', 'care-booking-block')]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if KiviCare plugin is active
|
||||
*
|
||||
* @return bool True if KiviCare is active, false otherwise
|
||||
*/
|
||||
private function is_kivicare_active()
|
||||
{
|
||||
// Check if KiviCare plugin is active
|
||||
if (!function_exists('is_plugin_active')) {
|
||||
include_once(ABSPATH . 'wp-admin/includes/plugin.php');
|
||||
}
|
||||
|
||||
return is_plugin_active('kivicare/kivicare.php') ||
|
||||
is_plugin_active('kivicare-clinic-management-system/kivicare.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Render KiviCare warning
|
||||
*/
|
||||
private function render_kivicare_warning()
|
||||
{
|
||||
?>
|
||||
<div class="wrap">
|
||||
<h1><?php esc_html_e('Care Booking Control', 'care-booking-block'); ?></h1>
|
||||
<div class="notice notice-error">
|
||||
<p>
|
||||
<?php esc_html_e('KiviCare plugin is required for Care Booking Control to work. Please install and activate KiviCare.', 'care-booking-block'); ?>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
}
|
||||
|
||||
/**
|
||||
* Get KiviCare doctors with restriction status
|
||||
*
|
||||
* @return array Array of doctors with restriction status
|
||||
*/
|
||||
private function get_kivicare_doctors()
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
// Get doctors from KiviCare (mock implementation)
|
||||
// In real implementation, this would query KiviCare tables
|
||||
$doctors = [];
|
||||
|
||||
// Get blocked doctors for status
|
||||
$blocked_doctors = $this->restriction_model->get_blocked_doctors();
|
||||
|
||||
// SECURITY: Mock doctors for testing with output escaping
|
||||
for ($i = 1; $i <= 10; $i++) {
|
||||
$doctors[] = [
|
||||
'id' => $i,
|
||||
'name' => esc_html("Dr. Test Doctor $i"),
|
||||
'email' => esc_html("doctor$i@clinic.com"),
|
||||
'is_blocked' => in_array($i, $blocked_doctors)
|
||||
];
|
||||
}
|
||||
|
||||
return $doctors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get KiviCare services with restriction status
|
||||
*
|
||||
* @param int $doctor_id Optional doctor ID to filter services
|
||||
* @return array Array of services with restriction status
|
||||
*/
|
||||
private function get_kivicare_services($doctor_id = null)
|
||||
{
|
||||
global $wpdb;
|
||||
|
||||
// Get services from KiviCare (mock implementation)
|
||||
$services = [];
|
||||
|
||||
if ($doctor_id) {
|
||||
// Get blocked services for this doctor
|
||||
$blocked_services = $this->restriction_model->get_blocked_services($doctor_id);
|
||||
|
||||
// SECURITY: Mock services for testing with output escaping
|
||||
for ($i = 1; $i <= 5; $i++) {
|
||||
$services[] = [
|
||||
'id' => $i,
|
||||
'name' => esc_html("Service $i"),
|
||||
'doctor_id' => $doctor_id,
|
||||
'is_blocked' => in_array($i, $blocked_services)
|
||||
];
|
||||
}
|
||||
} else {
|
||||
// SECURITY: Return all services with output escaping
|
||||
for ($i = 1; $i <= 20; $i++) {
|
||||
$services[] = [
|
||||
'id' => $i,
|
||||
'name' => esc_html("Service $i"),
|
||||
'doctor_id' => (($i - 1) % 10) + 1,
|
||||
'is_blocked' => false
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $services;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate KiviCare target exists
|
||||
*
|
||||
* @param string $type Target type
|
||||
* @param int $target_id Target ID
|
||||
* @param int $doctor_id Doctor ID (for services)
|
||||
* @return bool True if target exists, false otherwise
|
||||
*/
|
||||
private function validate_kivicare_target($type, $target_id, $doctor_id = null)
|
||||
{
|
||||
// SECURITY: Enhanced target validation with logging
|
||||
if (!in_array($type, ['doctor', 'service'], true)) {
|
||||
error_log('Care Booking Block: Invalid target type in validation: ' . $type);
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($target_id <= 0) {
|
||||
error_log('Care Booking Block: Invalid target_id in validation: ' . $target_id);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Mock validation - always return true for testing
|
||||
// In real implementation, this would check KiviCare tables with prepared statements
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* SECURITY: Rate limiting mechanism
|
||||
*
|
||||
* @param string $action Action being performed
|
||||
* @param int $max_requests Maximum requests allowed
|
||||
* @param int $time_window Time window in seconds (default: 60)
|
||||
* @return bool True if within limits, false if rate limited
|
||||
*/
|
||||
private function check_rate_limit($action, $max_requests = 30, $time_window = 60)
|
||||
{
|
||||
$user_id = get_current_user_id();
|
||||
$transient_key = 'care_booking_rate_limit_' . $action . '_' . $user_id;
|
||||
|
||||
$requests = get_transient($transient_key);
|
||||
|
||||
if ($requests === false) {
|
||||
// First request in time window
|
||||
set_transient($transient_key, 1, $time_window);
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($requests >= $max_requests) {
|
||||
error_log("Care Booking Block: Rate limit exceeded for action '$action' by user $user_id");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Increment counter
|
||||
set_transient($transient_key, $requests + 1, $time_window);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* SECURITY: Sanitize and validate admin page content
|
||||
*
|
||||
* @param mixed $data Data to sanitize
|
||||
* @return mixed Sanitized data
|
||||
*/
|
||||
private function sanitize_admin_data($data)
|
||||
{
|
||||
if (is_string($data)) {
|
||||
return sanitize_text_field($data);
|
||||
}
|
||||
|
||||
if (is_array($data)) {
|
||||
return array_map([$this, 'sanitize_admin_data'], $data);
|
||||
}
|
||||
|
||||
if (is_int($data)) {
|
||||
return absint($data);
|
||||
}
|
||||
|
||||
if (is_bool($data)) {
|
||||
return (bool) $data;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* SECURITY: Log security events
|
||||
*
|
||||
* @param string $event Event description
|
||||
* @param array $context Event context
|
||||
*/
|
||||
private function log_security_event($event, $context = [])
|
||||
{
|
||||
$log_entry = sprintf(
|
||||
'Care Booking Block Security: %s | User ID: %d | IP: %s | Context: %s',
|
||||
$event,
|
||||
get_current_user_id(),
|
||||
$_SERVER['REMOTE_ADDR'] ?? 'unknown',
|
||||
json_encode($context)
|
||||
);
|
||||
|
||||
error_log($log_entry);
|
||||
|
||||
// Trigger action for external security monitoring
|
||||
do_action('care_booking_security_event', $event, $context);
|
||||
}
|
||||
|
||||
/**
|
||||
* SECURITY: Validate WordPress environment
|
||||
*
|
||||
* @return bool True if environment is secure
|
||||
*/
|
||||
private function validate_environment()
|
||||
{
|
||||
// Check if we're in WordPress admin
|
||||
if (!is_admin()) {
|
||||
$this->log_security_event('Invalid environment: not admin area');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if user is logged in
|
||||
if (!is_user_logged_in()) {
|
||||
$this->log_security_event('Invalid environment: user not logged in');
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for multisite restrictions
|
||||
if (is_multisite() && !is_super_admin()) {
|
||||
$this->log_security_event('Invalid environment: multisite without super admin');
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* SECURITY: Enhanced error handling with security logging
|
||||
*
|
||||
* @param string $error_message Error message
|
||||
* @param array $context Error context
|
||||
*/
|
||||
private function handle_security_error($error_message, $context = [])
|
||||
{
|
||||
$this->log_security_event('Security Error: ' . $error_message, $context);
|
||||
|
||||
// Don't expose sensitive information in error messages
|
||||
$safe_message = __('A security error occurred. Please try again.', 'care-booking-block');
|
||||
wp_send_json_error(['message' => $safe_message]);
|
||||
wp_die();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user