aboutsummaryrefslogtreecommitdiffstats
path: root/tests/phpunit/includes/session/SessionTest.php
blob: f78cf8bd63f81c174a7259830c887c848b775c53 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<?php

namespace MediaWiki\Session;

use MediaWiki\User\User;
use MediaWikiIntegrationTestCase;
use Psr\Log\LogLevel;
use Wikimedia\TestingAccessWrapper;

/**
 * @group Session
 * @covers \MediaWiki\Session\Session
 */
class SessionTest extends MediaWikiIntegrationTestCase {

	public function testClear() {
		$session = TestUtils::getDummySession();
		$priv = TestingAccessWrapper::newFromObject( $session );

		$backend = $this->getMockBuilder( DummySessionBackend::class )
			->addMethods( [ 'canSetUser', 'setUser', 'save' ] )
			->getMock();
		$backend->expects( $this->once() )->method( 'canSetUser' )
			->willReturn( true );
		$backend->expects( $this->once() )->method( 'setUser' )
			->with( $this->callback( static function ( $user ) {
				return $user instanceof User && $user->isAnon();
			} ) );
		$backend->expects( $this->once() )->method( 'save' );
		$priv->backend = $backend;
		$session->clear();
		$this->assertSame( [], $backend->data );
		$this->assertTrue( $backend->dirty );

		$backend = $this->getMockBuilder( DummySessionBackend::class )
			->addMethods( [ 'canSetUser', 'setUser', 'save' ] )
			->getMock();
		$backend->data = [];
		$backend->expects( $this->once() )->method( 'canSetUser' )
			->willReturn( true );
		$backend->expects( $this->once() )->method( 'setUser' )
			->with( $this->callback( static function ( $user ) {
				return $user instanceof User && $user->isAnon();
			} ) );
		$backend->expects( $this->once() )->method( 'save' );
		$priv->backend = $backend;
		$session->clear();
		$this->assertFalse( $backend->dirty );

		$backend = $this->getMockBuilder( DummySessionBackend::class )
			->addMethods( [ 'canSetUser', 'setUser', 'save' ] )
			->getMock();
		$backend->expects( $this->once() )->method( 'canSetUser' )
			->willReturn( false );
		$backend->expects( $this->never() )->method( 'setUser' );
		$backend->expects( $this->once() )->method( 'save' );
		$priv->backend = $backend;
		$session->clear();
		$this->assertSame( [], $backend->data );
		$this->assertTrue( $backend->dirty );
	}

	public function testSecrets() {
		$logger = new \TestLogger;
		$session = TestUtils::getDummySession( null, -1, $logger );

		// Simple defaulting
		$this->assertEquals( 'defaulted', $session->getSecret( 'test', 'defaulted' ) );

		// Bad encrypted data
		$session->set( 'test', 'foobar' );
		$logger->setCollect( true );
		$this->assertEquals( 'defaulted', $session->getSecret( 'test', 'defaulted' ) );
		$logger->setCollect( false );
		$this->assertSame( [
			[ LogLevel::WARNING, 'Invalid sealed-secret format' ]
		], $logger->getBuffer() );
		$logger->clearBuffer();

		// Tampered data
		$session->setSecret( 'test', 'foobar' );
		$encrypted = $session->get( 'test' );
		$session->set( 'test', $encrypted . 'x' );
		$logger->setCollect( true );
		$this->assertEquals( 'defaulted', $session->getSecret( 'test', 'defaulted' ) );
		$logger->setCollect( false );
		$this->assertSame( [
			[ LogLevel::WARNING, 'Sealed secret has been tampered with, aborting.' ]
		], $logger->getBuffer() );
		$logger->clearBuffer();

		// Unserializable data
		$iv = random_bytes( 16 );
		[ $encKey, $hmacKey ] = TestingAccessWrapper::newFromObject( $session )->getSecretKeys();
		$ciphertext = openssl_encrypt( 'foobar', 'aes-256-ctr', $encKey, OPENSSL_RAW_DATA, $iv );
		$sealed = base64_encode( $iv ) . '.' . base64_encode( $ciphertext );
		$hmac = hash_hmac( 'sha256', $sealed, $hmacKey, true );
		$encrypted = base64_encode( $hmac ) . '.' . $sealed;
		$session->set( 'test', $encrypted );
		$this->assertEquals( 'defaulted', @$session->getSecret( 'test', 'defaulted' ) );
	}

	/**
	 * @dataProvider provideSecretsRoundTripping
	 */
	public function testSecretsRoundTripping( $data ) {
		$session = TestUtils::getDummySession();

		// Simple round-trip
		$session->setSecret( 'secret', $data );
		// Cast to strings because PHPUnit sometimes considers true as equal to a string,
		// depending on the other of the parameters (T317750)
		$raw = $session->get( 'secret' );
		$this->assertIsString( $raw );
		if ( is_scalar( $data ) ) {
			$this->assertNotSame( (string)$data, $raw );
		}
		$this->assertEquals( $data, $session->getSecret( 'secret', 'defaulted' ) );
	}

	public static function provideSecretsRoundTripping() {
		return [
			[ 'Foobar' ],
			[ 42 ],
			[ [ 'foo', 'bar' => 'baz', 'subarray' => [ 1, 2, 3 ] ] ],
			[ (object)[ 'foo', 'bar' => 'baz', 'subarray' => [ 1, 2, 3 ] ] ],
			[ true ],
			[ false ],
			[ null ],
		];
	}

}