transport = $transport instanceof Akkedli_V2_Session_Transport ? $transport : new Akkedli_V2_Session_Transport(); $this->entropy = is_callable($entropy) ? $entropy : 'random_bytes'; add_action('admin_post_' . self::ACTION, array($this, 'handle')); } public static function view() { $stored = Akkedli_V2_Session_State::load(); if (is_array($stored) && isset($stored['mode']) && 'legacy' === $stored['mode']) return false; if (is_array($stored)) return array('session' => $stored['session'], 'expected_store_url' => $stored['expected_store_url'], 'resuming' => true); $requested = Akkedli_V2_Session_State::requested(); return '' === $requested ? false : array('session' => $requested, 'expected_store_url' => home_url(), 'resuming' => false); } public function handle() { if (!isset($_SERVER['REQUEST_METHOD']) || 'POST' !== strtoupper( sanitize_text_field(wp_unslash($_SERVER['REQUEST_METHOD'])))) $this->redirect('session_error'); if (!current_user_can('manage_woocommerce')) { wp_die(esc_html__('You are not allowed to manage this connection.', 'akkedli-connector'), esc_html__('Permission denied', 'akkedli-connector'), array('response' => 403)); } $nonce = isset($_POST[self::NONCE_FIELD]) && is_string($_POST[self::NONCE_FIELD]) ? sanitize_text_field(wp_unslash($_POST[self::NONCE_FIELD])) : ''; if (!wp_verify_nonce($nonce, self::NONCE)) $this->redirect('session_error'); $session = isset($_POST['akkedli_session']) && is_string($_POST['akkedli_session']) ? strtolower(sanitize_text_field(wp_unslash($_POST['akkedli_session']))) : ''; $owner = Akkedli_Enrollment_Lock::acquire(); if (!is_string($owner)) $this->redirect('session_busy'); try { $notice = $this->process($session); } catch (Throwable $error) { unset($error); $notice = 'session_error'; } finally { Akkedli_Enrollment_Lock::release($owner); unset($owner, $session); } $this->redirect($notice); } private function process($session) { $stored = Akkedli_V2_Session_State::load(); if (is_array($stored) && isset($stored['mode']) && 'legacy' === $stored['mode']) return 'session_error'; if (is_array($stored)) { if (strtotime($stored['expires_at']) <= time()) { return Akkedli_V2_Session_Exchange::cleanup_terminal('session_expired'); } } if (!is_string($session) || !preg_match( '/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/D', $session)) return 'session_error'; if (is_array($stored)) { if (!hash_equals($stored['session'], $session)) return 'session_error'; $status = Akkedli_Connector_State::status(); if ('absent' === $status) return $this->begin($stored); if (in_array($status, array('v2_pending', 'v2'), true)) { $cleared = Akkedli_V2_Session_Exchange::clear_session('verifying'); if ('verifying' === $cleared && 'v2_pending' === $status) Akkedli_V2_Binding_Poller::schedule_current(); return $cleared; } return $this->resume($stored); } if ('absent' !== Akkedli_Connector_State::status()) return 'session_error'; $callback = Akkedli_V2_Callback_URL::derive(); if (!is_string($callback)) return 'session_error'; $secret = Akkedli_V2_Crypto::base64url_encode(call_user_func($this->entropy, 32)); $store = substr($callback, 0, -strlen('/wp-json/akkedli/v2/verification-result')); $state = array('session' => $session, 'bootstrap_secret' => $secret, 'callback_url' => $callback, 'expected_store_url' => $store, 'expires_at' => gmdate('c', time() + (10 * MINUTE_IN_SECONDS))); unset($secret); if (!Akkedli_V2_Session_Setup::create($state)) return 'session_error'; return $this->begin($state); } private function begin(array $state) { if ('absent' !== Akkedli_Connector_State::status()) return 'session_error'; $pending = array('schema_version' => 1, 'mode' => 'migration_pending', 'legacy_backup' => Akkedli_Connector_State::create_legacy_backup()); if (!Akkedli_Connector_State::replace($pending)) { if ('absent' === Akkedli_Connector_State::status()) Akkedli_V2_Session_Setup::delete_owned($state['session']); return 'session_error'; } return $this->resume($state); } private function resume(array $state) { if (Akkedli_V2_Session_Retry::waiting($state['session'])) return 'session_retry'; $claim = $this->transport->claim( $state['session'], $state['bootstrap_secret'], $state['callback_url'] ); if ('success' !== $claim['kind']) { return Akkedli_V2_Session_Exchange::advance($state, $claim, $this->transport); } if (!hash_equals($state['expected_store_url'], $claim['expected_store_url'])) { return 'session_pending'; } $state['expires_at'] = $claim['expires_at']; if (!Akkedli_V2_Session_State::save($state)) return 'session_error'; $status = $this->transport->status($state); return Akkedli_V2_Session_Exchange::advance($state, $status, $this->transport); } private function redirect($notice) { $allowed = array('verifying', 'session_pending', 'session_retry', 'session_failed', 'session_expired', 'session_busy', 'session_error'); if (!in_array($notice, $allowed, true)) $notice = 'session_error'; wp_safe_redirect(add_query_arg(array('page' => 'akkedli-connector', 'akkedli_session_notice' => $notice), admin_url('admin.php'))); exit; } }