Skip to content

Commit d70443d

Browse files
authored
fix: QLSessionHandler behaviour changes and QLSessionHandlerTest wpunit test added (#616)
* chore: QLSessionHandlerTest added * chore: WPCS compliance met * fix: JWT leeway set in unit test * fix: Docker environment salts updated * fix: salts restored * chore: WPCS compliance met * chore: JWT::leeway removed from tests * devops: Uncommented assertion in QLSessionHandlerTest * chore: WPCS compliance met
1 parent 118d183 commit d70443d

File tree

3 files changed

+169
-9
lines changed

3 files changed

+169
-9
lines changed

includes/utils/class-ql-session-handler.php

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,9 @@ public function init_session_token() {
138138
$this->set_customer_session_token( true );
139139
}
140140

141-
// Update session if its close to expiring.
142-
if ( time() > $this->_session_expiring ) {
143-
$this->set_session_expiration();
141+
// Update session expiration on each action.
142+
$this->set_session_expiration();
143+
if ( $token->exp < $this->set_session_expiration ) {
144144
$this->update_session_timestamp( $this->_customer_id, $this->_session_expiration );
145145
}
146146
} else {
@@ -241,6 +241,10 @@ public function get_session_header() {
241241
* @return string
242242
*/
243243
public function build_token() {
244+
if ( empty( $this->_session_issued ) ) {
245+
return false;
246+
}
247+
244248
/**
245249
* Determine the "not before" value for use in the token
246250
*
@@ -311,7 +315,7 @@ public function build_token() {
311315
* @param bool $set Should the session cookie be set.
312316
*/
313317
public function set_customer_session_token( $set ) {
314-
if ( $set ) {
318+
if ( ! empty( $this->_session_issued ) && $set ) {
315319
/**
316320
* Set callback session token for use in the HTTP response header and customer/user "sessionToken" field.
317321
*/
@@ -353,7 +357,7 @@ public function set_session_expiration() {
353357
time() + ( 3600 * 336 )
354358
);
355359
// 13 Days.
356-
$this->_session_expiring = $this->_session_expiration - ( 3600 * 24 );
360+
$this->_session_expiring = $this->_session_expiration - ( 3600 * 60 );
357361
}
358362

359363
/**
@@ -364,9 +368,14 @@ public function forget_session() {
364368
unset( $this->_token_to_be_sent );
365369
}
366370
wc_empty_cart();
367-
$this->_data = array();
368-
$this->_dirty = false;
369-
$this->_customer_id = $this->generate_customer_id();
371+
$this->_data = array();
372+
$this->_dirty = false;
373+
374+
// Start new session.
375+
$this->set_session_expiration();
376+
377+
// Get Customer ID.
378+
$this->_customer_id = is_user_logged_in() ? get_current_user_id() : $this->generate_customer_id();
370379
}
371380

372381
/**
@@ -386,6 +395,7 @@ public function save_if_dirty( $source, $args, $context, $info ) {
386395
// Update if user recently authenticated.
387396
if ( is_user_logged_in() && get_current_user_id() !== $this->_customer_id ) {
388397
$this->_customer_id = get_current_user_id();
398+
$this->_dirty = true;
389399
}
390400

391401
// Bail if no changes.
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
<?php
2+
/**
3+
* Unit test for QL_Session_Handler
4+
*/
5+
6+
use Firebase\JWT\JWT;
7+
use WPGraphQL\WooCommerce\Utils\QL_Session_Handler;
8+
9+
if ( ! defined( 'GRAPHQL_WOOCOMMERCE_SECRET_KEY' ) ) {
10+
define( 'GRAPHQL_WOOCOMMERCE_SECRET_KEY', 'graphql-woo-cart-session' );
11+
}
12+
13+
class QLSessionHandlerTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTestCase {
14+
public function tearDown(): void {
15+
unset( $_SERVER );
16+
17+
// after
18+
parent::tearDown();
19+
}
20+
21+
// Tests
22+
public function test_initializes() {
23+
// Create session handler.
24+
$session = new QL_Session_Handler();
25+
26+
$this->assertInstanceOf( QL_Session_Handler::class, $session );
27+
}
28+
29+
public function test_init_session_token() {
30+
// Create session handler.
31+
$session = new QL_Session_Handler();
32+
33+
// Assert session hasn't started.
34+
$this->assertFalse( $session->has_session(), 'Shouldn\'t have a session yet' );
35+
36+
// Initialize session.
37+
$session->init_session_token();
38+
39+
// Assert session has started.
40+
$this->assertTrue( $session->has_session(), 'Should have session.' );
41+
42+
// Get token for future request.
43+
$old_token = $session->build_token();
44+
$decoded_old_token = JWT::decode( $old_token, GRAPHQL_WOOCOMMERCE_SECRET_KEY, array( 'HS256' ) );
45+
46+
// Sent token to HTTP header to simulate a new request.
47+
$_SERVER['HTTP_WOOCOMMERCE_SESSION'] = 'Session ' . $old_token;
48+
49+
// Stale for 5 seconds so timers can update.
50+
usleep( 1000000 );
51+
52+
// Initialize session token for next request.
53+
$session->init_session_token();
54+
$new_token = $session->build_token();
55+
$decoded_new_token = JWT::decode( $new_token, GRAPHQL_WOOCOMMERCE_SECRET_KEY, array( 'HS256' ) );
56+
57+
// Assert new token is different than old token.
58+
$this->assertNotEquals( $old_token, $new_token, 'New token should not match token from last request.' );
59+
$this->assertGreaterThan( $decoded_old_token->exp, $decoded_new_token->exp );
60+
}
61+
62+
public function test_get_session_token() {
63+
// Create session handler.
64+
$session = new QL_Session_Handler();
65+
66+
// Expect token to be null.
67+
$null_token = $session->get_session_token();
68+
$this->assertFalse( $null_token, 'No token should exist.' );
69+
70+
// Set token in header.
71+
$session->init_session_token();
72+
$_SERVER['HTTP_WOOCOMMERCE_SESSION'] = 'Session ' . $session->build_token();
73+
74+
// Expect token to be value.
75+
$token = $session->get_session_token();
76+
$this->assertObjectHasAttribute( 'iat', $token );
77+
$this->assertObjectHasAttribute( 'exp', $token );
78+
$this->assertObjectHasAttribute( 'data', $token );
79+
}
80+
81+
public function test_get_session_header() {
82+
// Create session handler.
83+
$session = new QL_Session_Handler();
84+
$session->init_session_token();
85+
86+
// Get the Auth header.
87+
$null_header = $session->get_session_header();
88+
89+
$this->assertFalse( $null_header, 'No HTTP Header with session token should exist.' );
90+
91+
// Set token in header.
92+
$_SERVER['HTTP_WOOCOMMERCE_SESSION'] = 'Session ' . $session->build_token();
93+
94+
$this->assertIsString( $session->get_session_header() );
95+
}
96+
97+
public function test_build_token() {
98+
// Create session handler.
99+
$session = new QL_Session_Handler();
100+
101+
// Should be invalid if run before initialization.
102+
$invalid_token = $session->build_token();
103+
$this->assertFalse( $invalid_token, 'Should be an invalid session token' );
104+
105+
// Should valid when run after initialization.
106+
$session->init_session_token();
107+
$token = $session->build_token();
108+
109+
$decode_token = JWT::decode( $token, GRAPHQL_WOOCOMMERCE_SECRET_KEY, array( 'HS256' ) );
110+
$this->assertObjectHasAttribute( 'iat', $decode_token );
111+
$this->assertObjectHasAttribute( 'exp', $decode_token );
112+
$this->assertObjectHasAttribute( 'data', $decode_token );
113+
114+
$this->assertEquals( $token, $session->build_token() );
115+
}
116+
117+
public function test_set_customer_session_token() {
118+
// Create session handler.
119+
$session = new QL_Session_Handler();
120+
121+
// Should fail to set headers if run before initialization.
122+
$session->set_customer_session_token( true );
123+
$graphql_response_headers = apply_filters( 'graphql_response_headers_to_send', array() );
124+
$this->assertArrayNotHasKey( 'woocommerce-session', $graphql_response_headers );
125+
126+
// Should success when run after initialization.
127+
$session->init_session_token();
128+
$graphql_response_headers = apply_filters( 'graphql_response_headers_to_send', array() );
129+
$this->assertArrayHasKey( 'woocommerce-session', $graphql_response_headers );
130+
}
131+
132+
public function test_forget_session() {
133+
// Create session handler.
134+
$session = new QL_Session_Handler();
135+
$session->init_session_token();
136+
137+
// Get old token
138+
$old_token = $session->build_token();
139+
$this->assertIsString( $old_token );
140+
141+
// Forget session
142+
$session->forget_session();
143+
144+
// Get new token.
145+
$new_token = $session->build_token();
146+
$this->assertIsString( $old_token );
147+
148+
$this->assertNotEquals( $old_token, $new_token, 'Tokens should not match' );
149+
}
150+
}

tests/wpunit/bootstrap.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?php
22

33
// Turn off "QL_SESSION_HANDLER" for unit tests.
4-
define( 'NO_QL_SESSION_HANDLER', true );
4+
//define( 'NO_QL_SESSION_HANDLER', true );
55

66
add_filter(
77
'graphql_request_results',

0 commit comments

Comments
 (0)