Completed
Push — 4 ( 6c0917...fa3556 )
by Guy
40s queued 26s
created

CookieTest::testValidateSameSiteWarning()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 10
nc 1
nop 0
dl 0
loc 14
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Control\Tests;
4
5
use Exception;
6
use LogicException;
7
use Monolog\Logger;
8
use Psr\Log\LoggerInterface;
9
use SilverStripe\Core\Injector\Injector;
10
use SilverStripe\Dev\SapphireTest;
11
use SilverStripe\Control\CookieJar;
12
use SilverStripe\Control\Cookie;
13
use SilverStripe\Control\Director;
14
15
class CookieTest extends SapphireTest
16
{
17
18
    protected function setUp(): void
19
    {
20
        parent::setUp();
21
        Injector::inst()->registerService(new CookieJar($_COOKIE), 'SilverStripe\\Control\\Cookie_Backend');
22
    }
23
24
    /**
25
     * Check a new cookie inst will be loaded with the superglobal by default
26
     */
27
    public function testCheckNewInstTakesSuperglobal()
28
    {
29
        //store the superglobal state
30
        $existingCookies = $_COOKIE;
31
32
        //set a mock state for the superglobal
33
        $_COOKIE = [
34
            'cookie1' => 1,
35
            'cookie2' => 'cookies',
36
            'cookie3' => 'test',
37
            'cookie_4' => 'value',
38
        ];
39
40
        Injector::inst()->unregisterNamedObject('SilverStripe\\Control\\Cookie_Backend');
41
42
        $this->assertEquals($_COOKIE['cookie1'], Cookie::get('cookie1'));
43
        $this->assertEquals($_COOKIE['cookie2'], Cookie::get('cookie2'));
44
        $this->assertEquals($_COOKIE['cookie3'], Cookie::get('cookie3'));
45
        $this->assertEquals($_COOKIE['cookie_4'], Cookie::get('cookie.4'));
46
        $this->assertEquals($_COOKIE['cookie_4'], Cookie::get('cookie_4'));
47
48
        //for good measure check the CookieJar hasn't stored anything extra
49
        $this->assertEquals($_COOKIE, Cookie::get_inst()->getAll(false));
50
51
        //restore the superglobal state
52
        $_COOKIE = $existingCookies;
53
    }
54
55
    /**
56
     * Check we don't mess with super globals when manipulating cookies
57
     *
58
     * State should be managed separately to the super global
59
     */
60
    public function testCheckSuperglobalsArentTouched()
61
    {
62
63
        //store the current state
64
        $before = $_COOKIE;
65
66
        //change some cookies
67
        Cookie::set('cookie', 'not me');
68
        Cookie::force_expiry('cookie2');
69
70
        //assert it hasn't changed
71
        $this->assertEquals($before, $_COOKIE);
72
    }
73
74
    /**
75
     * Check we can actually change a backend
76
     */
77
    public function testChangeBackend()
78
    {
79
80
        Cookie::set('test', 'testvalue');
81
82
        $this->assertEquals('testvalue', Cookie::get('test'));
83
84
        Injector::inst()->registerService(new CookieJar([]), 'SilverStripe\\Control\\Cookie_Backend');
85
86
        $this->assertEmpty(Cookie::get('test'));
87
    }
88
89
    /**
90
     * Check we can actually get the backend inst out
91
     */
92
    public function testGetInst()
93
    {
94
95
        $inst = new CookieJar(['test' => 'testvalue']);
96
97
        Injector::inst()->registerService($inst, 'SilverStripe\\Control\\Cookie_Backend');
98
99
        $this->assertEquals($inst, Cookie::get_inst());
100
101
        $this->assertEquals('testvalue', Cookie::get('test'));
102
    }
103
104
    /**
105
     * Test that we can set and get cookies
106
     */
107
    public function testSetAndGet()
108
    {
109
        $this->assertEmpty(Cookie::get('testCookie'));
110
111
        //set a test cookie
112
        Cookie::set('testCookie', 'testVal');
113
114
        //make sure it was set
115
        $this->assertEquals('testVal', Cookie::get('testCookie'));
116
117
        //make sure we can distinguise it from ones that were "existing"
118
        $this->assertEmpty(Cookie::get('testCookie', false));
119
    }
120
121
    /**
122
     * Test that we can distinguish between vars that were loaded on instantiation
123
     * and those added later
124
     */
125
    public function testExistingVersusNew()
126
    {
127
        //load with a cookie
128
        $cookieJar = new CookieJar(
129
            [
130
                'cookieExisting' => 'i woz here',
131
            ]
132
        );
133
        Injector::inst()->registerService($cookieJar, 'SilverStripe\\Control\\Cookie_Backend');
134
135
        //set a new cookie
136
        Cookie::set('cookieNew', 'i am new');
137
138
        //check we can fetch new and old cookie values
139
        $this->assertEquals('i woz here', Cookie::get('cookieExisting'));
140
        $this->assertEquals('i woz here', Cookie::get('cookieExisting', false));
141
        $this->assertEquals('i am new', Cookie::get('cookieNew'));
142
        //there should be no original value for the new cookie
143
        $this->assertEmpty(Cookie::get('cookieNew', false));
144
145
        //change the existing cookie, can we fetch the new and old value
146
        Cookie::set('cookieExisting', 'i woz changed');
147
148
        $this->assertEquals('i woz changed', Cookie::get('cookieExisting'));
149
        $this->assertEquals('i woz here', Cookie::get('cookieExisting', false));
150
151
        //check we can get all cookies
152
        $this->assertEquals(
153
            [
154
                'cookieExisting' => 'i woz changed',
155
                'cookieNew' => 'i am new',
156
            ],
157
            Cookie::get_all()
158
        );
159
160
        //check we can get all original cookies
161
        $this->assertEquals(
162
            [
163
                'cookieExisting' => 'i woz here',
164
            ],
165
            Cookie::get_all(false)
166
        );
167
    }
168
169
    /**
170
     * Check we can remove cookies and we can access their original values
171
     */
172
    public function testForceExpiry()
173
    {
174
        //load an existing cookie
175
        $cookieJar = new CookieJar(
176
            [
177
                'cookieExisting' => 'i woz here',
178
            ]
179
        );
180
        Injector::inst()->registerService($cookieJar, 'SilverStripe\\Control\\Cookie_Backend');
181
182
        //make sure it's available
183
        $this->assertEquals('i woz here', Cookie::get('cookieExisting'));
184
185
        //remove the cookie
186
        Cookie::force_expiry('cookieExisting');
187
188
        //check it's gone
189
        $this->assertEmpty(Cookie::get('cookieExisting'));
190
191
        //check we can get it's original value
192
        $this->assertEquals('i woz here', Cookie::get('cookieExisting', false));
193
194
195
        //check we can add a new cookie and remove it and it doesn't leave any phantom values
196
        Cookie::set('newCookie', 'i am new');
197
198
        //check it's set by not received
199
        $this->assertEquals('i am new', Cookie::get('newCookie'));
200
        $this->assertEmpty(Cookie::get('newCookie', false));
201
202
        //remove it
203
        Cookie::force_expiry('newCookie');
204
205
        //check it's neither set nor reveived
206
        $this->assertEmpty(Cookie::get('newCookie'));
207
        $this->assertEmpty(Cookie::get('newCookie', false));
208
    }
209
210
    /**
211
     * Check that warnings are not logged for https requests and when samesite is not "None"
212
     * Test passes if no warning is logged
213
     */
214
    public function testValidateSameSiteNoWarning(): void
215
    {
216
        // Throw an exception when a warning is logged so we can catch it
217
        $mockLogger = $this->getMockBuilder(Logger::class)->setConstructorArgs(['testLogger'])->getMock();
218
        $catchMessage = 'A warning was logged';
219
        $mockLogger->expects($this->never())
220
            ->method('warning')
221
            ->willThrowException(new Exception($catchMessage));
222
        Injector::inst()->registerService($mockLogger, LoggerInterface::class);
223
224
        // Only samesite === 'None' should log a warning on non-https requests
225
        Director::config()->set('alternate_base_url', 'http://insecure.example.com/');
226
        Cookie::validateSameSite('Lax');
227
        Cookie::validateSameSite('Strict');
228
229
        // There should be no warnings logged for secure requests
230
        Director::config()->set('alternate_base_url', 'https://secure.example.com/');
231
        Cookie::validateSameSite('None');
232
        Cookie::validateSameSite('Lax');
233
        Cookie::validateSameSite('Strict');
234
    }
235
236
    /**
237
     * Check whether warnings are correctly logged for non-https requests and samesite === "None"
238
     */
239
    public function testValidateSameSiteWarning(): void
240
    {
241
        // Throw an exception when a warning is logged so we can catch it
242
        $mockLogger = $this->getMockBuilder(Logger::class)->setConstructorArgs(['testLogger'])->getMock();
243
        $catchMessage = 'A warning was logged';
244
        $mockLogger->expects($this->once())
245
            ->method('warning')
246
            ->willThrowException(new Exception($catchMessage));
247
        Injector::inst()->registerService($mockLogger, LoggerInterface::class);
248
        Director::config()->set('alternate_base_url', 'http://insecure.example.com/');
249
250
        $this->expectException(Exception::class);
251
        $this->expectExceptionMessage($catchMessage);
252
        Cookie::validateSameSite('None');
253
    }
254
255
    /**
256
     * An exception should be thrown for an empty samesite value
257
     */
258
    public function testValidateSameSiteInvalidEmpty(): void
259
    {
260
        $this->expectException(LogicException::class);
261
        Cookie::validateSameSite('');
262
    }
263
264
    /**
265
     * An exception should be thrown for an invalid samesite value
266
     */
267
    public function testValidateSameSiteInvalidNotEmpty(): void
268
    {
269
        $this->expectException(LogicException::class);
270
        Cookie::validateSameSite('invalid');
271
    }
272
}
273