PluginProbe
WCPOS – Point of Sale (POS) plugin for WooCommerce / trunk
WCPOS – Point of Sale (POS) plugin for WooCommerce vtrunk
1.10.13 1.10.14 1.10.12 1.10.11 1.10.10 1.10.9 1.10.8 untagged-3d9b7ccddc54df87c672 1.10.7 1.10.6 1.10.5 1.10.3 1.10.4 1.10.2 1.10.1 1.10.0 1.9.17 1.9.15 1.9.16 1.9.14 1.9.13 1.9.12 1.9.11 1.9.10 1.9.9 All 158 releases
woocommerce-pos / includes / Services / Session_Context.php

Session_Context.php in WCPOS – Point of Sale (POS) plugin for WooCommerce trunk, at includes/Services/Session_Context.php

201 lines 5.6 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Session Context.
4 *
5 * @package WCPOS\WooCommercePOS
6 */
7
8 namespace WCPOS\WooCommercePOS\Services;
9
10 /**
11 * Immutable snapshot of the request state a login session is recorded against.
12 *
13 * The Auth service used to read `$_SERVER` and `$_REQUEST` inline while storing
14 * a refresh token, which made the device-metadata rules impossible to test
15 * without mutating superglobals. This value object is the single place those
16 * reads happen: `from_request()` captures them, everything downstream takes a
17 * `Session_Context` and can be handed a hand-built one in tests.
18 *
19 * The reads in `from_request()` are a faithful port of the previous inline
20 * code, superglobal for superglobal, including the use of `$_REQUEST` rather
21 * than `$_GET` — the native apps post their device metadata as part of the auth
22 * flow, so narrowing the source would change behaviour.
23 */
24 class Session_Context {
25 /**
26 * The raw (sanitized) user agent string.
27 *
28 * @var string
29 */
30 private $user_agent;
31
32 /**
33 * Explicit platform declaration from a native app: ios, android, electron or web.
34 *
35 * @var string
36 */
37 private $platform;
38
39 /**
40 * Explicit app version declaration from a native app.
41 *
42 * @var string
43 */
44 private $version;
45
46 /**
47 * Explicit build number declaration from a native app.
48 *
49 * @var string
50 */
51 private $build;
52
53 /**
54 * The client IP address, or an empty string when it could not be determined.
55 *
56 * @var string
57 */
58 private $ip;
59
60 /**
61 * Constructor.
62 *
63 * @param string $user_agent The user agent string.
64 * @param string $platform Explicit platform declaration.
65 * @param string $version Explicit app version declaration.
66 * @param string $build Explicit build number declaration.
67 * @param string $ip The client IP address.
68 */
69 public function __construct(
70 string $user_agent = '',
71 string $platform = '',
72 string $version = '',
73 string $build = '',
74 string $ip = ''
75 ) {
76 $this->user_agent = $user_agent;
77 $this->platform = $platform;
78 $this->version = $version;
79 $this->build = $build;
80 $this->ip = $ip;
81 }
82
83 /**
84 * Capture the session context from the current request.
85 *
86 * Both arrays default to the corresponding superglobal. They are only
87 * parameters so tests can supply request state without mutating globals;
88 * production callers pass nothing.
89 *
90 * @param null|array $server Server/header state, defaults to $_SERVER.
91 * @param null|array $request Request parameters, defaults to $_REQUEST.
92 *
93 * @return self
94 */
95 public static function from_request( ?array $server = null, ?array $request = null ): self {
96 // phpcs:disable WordPress.Security.NonceVerification.Recommended -- Read-only capture of client-declared device metadata; the request itself is authenticated by the auth flow.
97 $server = null === $server ? $_SERVER : $server;
98 $request = null === $request ? $_REQUEST : $request;
99 // phpcs:enable WordPress.Security.NonceVerification.Recommended
100
101 $user_agent = isset( $server['HTTP_USER_AGENT'] ) ? sanitize_text_field( wp_unslash( $server['HTTP_USER_AGENT'] ) ) : '';
102
103 // Explicit platform declaration from native apps (passed as a param in the auth request).
104 $platform = isset( $request['platform'] ) ? sanitize_text_field( wp_unslash( $request['platform'] ) ) : '';
105 $version = isset( $request['version'] ) ? sanitize_text_field( wp_unslash( $request['version'] ) ) : '';
106 $build = isset( $request['build'] ) ? sanitize_text_field( wp_unslash( $request['build'] ) ) : '';
107
108 return new self( $user_agent, $platform, $version, $build, self::client_ip_from_request( $server ) );
109 }
110
111 /**
112 * Read the client IP address from the current request.
113 *
114 * Proxy headers are checked in a fixed precedence: Cloudflare, then
115 * X-Forwarded-For, then X-Real-IP, then the raw remote address. The first
116 * header that is present wins, and a comma-separated list is reduced to its
117 * first entry. Anything that does not validate as an IP becomes ''.
118 *
119 * @param null|array $server Server/header state, defaults to $_SERVER.
120 *
121 * @return string
122 */
123 public static function client_ip_from_request( ?array $server = null ): string {
124 $server = null === $server ? $_SERVER : $server;
125 $ip_address = '';
126
127 // Check for various proxy headers.
128 $headers = array(
129 'HTTP_CF_CONNECTING_IP', // Cloudflare.
130 'HTTP_X_FORWARDED_FOR',
131 'HTTP_X_REAL_IP',
132 'REMOTE_ADDR',
133 );
134
135 foreach ( $headers as $header ) {
136 if ( ! empty( $server[ $header ] ) ) {
137 $ip_address = sanitize_text_field( wp_unslash( $server[ $header ] ) );
138 // Handle comma-separated IPs (X-Forwarded-For can contain multiple IPs).
139 if ( false !== strpos( $ip_address, ',' ) ) {
140 $ip_parts = explode( ',', $ip_address );
141 $ip_address = trim( $ip_parts[0] );
142 }
143
144 break;
145 }
146 }
147
148 // Validate and sanitize IP.
149 if ( filter_var( $ip_address, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_IPV6 ) ) {
150 return $ip_address;
151 }
152
153 return '';
154 }
155
156 /**
157 * Get the user agent string.
158 *
159 * @return string
160 */
161 public function get_user_agent(): string {
162 return $this->user_agent;
163 }
164
165 /**
166 * Get the explicit platform declaration.
167 *
168 * @return string
169 */
170 public function get_platform(): string {
171 return $this->platform;
172 }
173
174 /**
175 * Get the explicit app version declaration.
176 *
177 * @return string
178 */
179 public function get_version(): string {
180 return $this->version;
181 }
182
183 /**
184 * Get the explicit build number declaration.
185 *
186 * @return string
187 */
188 public function get_build(): string {
189 return $this->build;
190 }
191
192 /**
193 * Get the client IP address.
194 *
195 * @return string
196 */
197 public function get_ip(): string {
198 return $this->ip;
199 }
200 }
201