PluginProbe
ووسلام – همگام سازی ووکامرس و باسلام / 1.10.19
ووسلام – همگام سازی ووکامرس و باسلام v1.10.19
1.10.19 1.10.20 1.10.18 1.10.17 1.10.15 1.10.14 1.10.13 1.10.12 1.10.10 1.10.9 1.10.8 1.10.7 1.10.6 1.10.5 1.10.4 1.10.3 1.10.2 1.10.1 1.10.0 1.9.2 1.9.1 1.9.0 1.8.8 1.8.5 1.8.6 All 53 releases
← All changes | includes/Admin/Settings/OAuthManager.php +55 -1 1.9.11.10.19 View file →
@@ -8,8 +8,50 @@
8 8 defined('ABSPATH') || exit;
9 9
10 10 class OAuthManager
11 11 {
12 + /** Prefix for the per-user transient holding a pending OAuth authorization. */
13 + const OAUTH_STATE_TRANSIENT = 'sync_basalam_oauth_state_';
14 +
15 + /** Lifetime of a pending OAuth authorization — the SSO round-trip window. */
16 + const OAUTH_STATE_TTL = 600; // 10 * MINUTE_IN_SECONDS
17 +
18 + /**
19 + * Remember that the current admin has just started an OAuth authorization.
20 + *
21 + * This is called only from the nonce-protected initiation flow, so the
22 + * marker it stores cannot be planted by a forged cross-site request. The
23 + * callback later requires (and consumes) this marker, which is what turns
24 + * the token-saving callback from "always forgeable" into "only valid for a
25 + * flow this admin actually started".
26 + */
27 + public static function issueOauthState()
28 + {
29 + $state = wp_generate_password(64, false);
30 + set_transient(self::OAUTH_STATE_TRANSIENT . get_current_user_id(), $state, self::OAUTH_STATE_TTL);
31 +
32 + return $state;
33 + }
34 +
35 + /**
36 + * Validate and consume the pending OAuth authorization for the current user.
37 + *
38 + * Single use: the marker is deleted whether or not it was present, so a
39 + * replayed or forged callback cannot reuse it.
40 + */
41 + private static function verifyOauthState()
42 + {
43 + $key = self::OAUTH_STATE_TRANSIENT . get_current_user_id();
44 + $expected = get_transient($key);
45 + delete_transient($key);
46 +
47 + // The token exchange is routed back through the Hamsalam proxy, which
48 + // consumes the SSO "state" (the site URL) and does not forward a secret
49 + // we control. The single-use marker set during the authenticated
50 + // initiation is therefore the value that authorises the write.
51 + return ! empty($expected);
52 + }
53 +
12 54 public function getOauthData()
13 55 {
14 56 $oauthDataUrl = apply_filters('sync_basalam_oauth_data_url', Endpoints::HAMSALAM_OAUTH_DATA);
15 57 $defaultClientId = apply_filters('sync_basalam_oauth_default_client_id', 779);
@@ -32,8 +74,20 @@
32 74 }
33 75
34 76 public static function saveOauthData()
35 77 {
78 + // CSRF protection: this callback performs a state-changing write from a
79 + // plain GET, so it must be tied to an OAuth flow the current admin
80 + // actually initiated. Without this an attacker could lure a logged-in
81 + // admin to the callback URL and overwrite the stored Basalam credentials.
82 + if (! current_user_can('manage_options') || ! self::verifyOauthState()) {
83 + wp_die(
84 + esc_html__('درخواست نامعتبر است.', 'sync-basalam'),
85 + esc_html__('خطای امنیتی', 'sync-basalam'),
86 + ['response' => 403]
87 + );
88 + }
89 +
36 90 $isVendor = isset($_GET['is_vendor']) ? sanitize_text_field(wp_unslash($_GET['is_vendor'])) : true;
37 91 $vendorId = isset($_GET['vendor_id']) ? sanitize_text_field(intval($_GET['vendor_id'])) : null;
38 92 $hamsalamToken = isset($_GET['hamsalam_token']) ? sanitize_text_field(wp_unslash($_GET['hamsalam_token'])) : null;
39 93 $hamsalamBusinessId = isset($_GET['hamsalam_business_id']) ? sanitize_text_field(wp_unslash($_GET['hamsalam_business_id'])) : null;
@@ -72,9 +126,9 @@
72 126 {
73 127 $oauthData = $this->getOauthData();
74 128 $siteUrl = get_site_url();
75 129
76 - $scopes = apply_filters('sync_basalam_oauth_scopes', "vendor.product.write vendor.parcel.write customer.profile.read vendor.profile.read vendor.parcel.read vendor.profile.write customer.chat.read customer.chat.write");
130 + $scopes = apply_filters('sync_basalam_oauth_scopes', "vendor.product.write vendor.parcel.write customer.profile.read vendor.profile.read vendor.parcel.read vendor.profile.write customer.chat.read customer.chat.write customer.identity.read");
77 131
78 132 return [
79 133 'redirect_uri' => $oauthData['redirect_uri'],
80 134 'url_req_token' => Endpoints::oauthLoginUrl(