RCFilters: Convert saved queries from filters to parameters
[lhc/web/wiklou.git] / tests / phpunit / includes / auth / ThrottlerTest.php
1 <?php
2
3 namespace MediaWiki\Auth;
4
5 use BagOStuff;
6 use HashBagOStuff;
7 use InvalidArgumentException;
8 use Psr\Log\AbstractLogger;
9 use Psr\Log\LoggerInterface;
10 use Psr\Log\NullLogger;
11 use Wikimedia\TestingAccessWrapper;
12
13 /**
14 * @group AuthManager
15 * @covers MediaWiki\Auth\Throttler
16 */
17 class ThrottlerTest extends \MediaWikiTestCase {
18 public function testConstructor() {
19 $cache = new \HashBagOStuff();
20 $logger = $this->getMockBuilder( AbstractLogger::class )
21 ->setMethods( [ 'log' ] )
22 ->getMockForAbstractClass();
23
24 $throttler = new Throttler(
25 [ [ 'count' => 123, 'seconds' => 456 ] ],
26 [ 'type' => 'foo', 'cache' => $cache ]
27 );
28 $throttler->setLogger( $logger );
29 $throttlerPriv = TestingAccessWrapper::newFromObject( $throttler );
30 $this->assertSame( [ [ 'count' => 123, 'seconds' => 456 ] ], $throttlerPriv->conditions );
31 $this->assertSame( 'foo', $throttlerPriv->type );
32 $this->assertSame( $cache, $throttlerPriv->cache );
33 $this->assertSame( $logger, $throttlerPriv->logger );
34
35 $throttler = new Throttler( [ [ 'count' => 123, 'seconds' => 456 ] ] );
36 $throttler->setLogger( new NullLogger() );
37 $throttlerPriv = TestingAccessWrapper::newFromObject( $throttler );
38 $this->assertSame( [ [ 'count' => 123, 'seconds' => 456 ] ], $throttlerPriv->conditions );
39 $this->assertSame( 'custom', $throttlerPriv->type );
40 $this->assertInstanceOf( BagOStuff::class, $throttlerPriv->cache );
41 $this->assertInstanceOf( LoggerInterface::class, $throttlerPriv->logger );
42
43 $this->setMwGlobals( [ 'wgPasswordAttemptThrottle' => [ [ 'count' => 321,
44 'seconds' => 654 ] ] ] );
45 $throttler = new Throttler();
46 $throttler->setLogger( new NullLogger() );
47 $throttlerPriv = TestingAccessWrapper::newFromObject( $throttler );
48 $this->assertSame( [ [ 'count' => 321, 'seconds' => 654 ] ], $throttlerPriv->conditions );
49 $this->assertSame( 'password', $throttlerPriv->type );
50 $this->assertInstanceOf( BagOStuff::class, $throttlerPriv->cache );
51 $this->assertInstanceOf( LoggerInterface::class, $throttlerPriv->logger );
52
53 try {
54 new Throttler( [], [ 'foo' => 1, 'bar' => 2, 'baz' => 3 ] );
55 $this->fail( 'Expected exception not thrown' );
56 } catch ( \InvalidArgumentException $ex ) {
57 $this->assertSame( 'unrecognized parameters: foo, bar, baz', $ex->getMessage() );
58 }
59 }
60
61 /**
62 * @dataProvider provideNormalizeThrottleConditions
63 */
64 public function testNormalizeThrottleConditions( $condition, $normalized ) {
65 $throttler = new Throttler( $condition );
66 $throttler->setLogger( new NullLogger() );
67 $throttlerPriv = TestingAccessWrapper::newFromObject( $throttler );
68 $this->assertSame( $normalized, $throttlerPriv->conditions );
69 }
70
71 public function provideNormalizeThrottleConditions() {
72 return [
73 [
74 [],
75 [],
76 ],
77 [
78 [ 'count' => 1, 'seconds' => 2 ],
79 [ [ 'count' => 1, 'seconds' => 2 ] ],
80 ],
81 [
82 [ [ 'count' => 1, 'seconds' => 2 ], [ 'count' => 2, 'seconds' => 3 ] ],
83 [ [ 'count' => 1, 'seconds' => 2 ], [ 'count' => 2, 'seconds' => 3 ] ],
84 ],
85 ];
86 }
87
88 public function testNormalizeThrottleConditions2() {
89 $priv = TestingAccessWrapper::newFromClass( Throttler::class );
90 $this->assertSame( [], $priv->normalizeThrottleConditions( null ) );
91 $this->assertSame( [], $priv->normalizeThrottleConditions( 'bad' ) );
92 }
93
94 public function testIncrease() {
95 $cache = new \HashBagOStuff();
96 $throttler = new Throttler( [
97 [ 'count' => 2, 'seconds' => 10, ],
98 [ 'count' => 4, 'seconds' => 15, 'allIPs' => true ],
99 ], [ 'cache' => $cache ] );
100 $throttler->setLogger( new NullLogger() );
101
102 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
103 $this->assertFalse( $result, 'should not throttle' );
104
105 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
106 $this->assertFalse( $result, 'should not throttle' );
107
108 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
109 $this->assertSame( [ 'throttleIndex' => 0, 'count' => 2, 'wait' => 10 ], $result );
110
111 $result = $throttler->increase( 'OtherUser', '1.2.3.4' );
112 $this->assertFalse( $result, 'should not throttle' );
113
114 $result = $throttler->increase( 'SomeUser', '2.3.4.5' );
115 $this->assertFalse( $result, 'should not throttle' );
116
117 $result = $throttler->increase( 'SomeUser', '3.4.5.6' );
118 $this->assertFalse( $result, 'should not throttle' );
119
120 $result = $throttler->increase( 'SomeUser', '3.4.5.6' );
121 $this->assertSame( [ 'throttleIndex' => 1, 'count' => 4, 'wait' => 15 ], $result );
122 }
123
124 public function testZeroCount() {
125 $cache = new \HashBagOStuff();
126 $throttler = new Throttler( [ [ 'count' => 0, 'seconds' => 10 ] ], [ 'cache' => $cache ] );
127 $throttler->setLogger( new NullLogger() );
128
129 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
130 $this->assertFalse( $result, 'should not throttle, count=0 is ignored' );
131
132 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
133 $this->assertFalse( $result, 'should not throttle, count=0 is ignored' );
134
135 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
136 $this->assertFalse( $result, 'should not throttle, count=0 is ignored' );
137 }
138
139 public function testNamespacing() {
140 $cache = new \HashBagOStuff();
141 $throttler1 = new Throttler( [ [ 'count' => 1, 'seconds' => 10 ] ],
142 [ 'cache' => $cache, 'type' => 'foo' ] );
143 $throttler2 = new Throttler( [ [ 'count' => 1, 'seconds' => 10 ] ],
144 [ 'cache' => $cache, 'type' => 'foo' ] );
145 $throttler3 = new Throttler( [ [ 'count' => 1, 'seconds' => 10 ] ],
146 [ 'cache' => $cache, 'type' => 'bar' ] );
147 $throttler1->setLogger( new NullLogger() );
148 $throttler2->setLogger( new NullLogger() );
149 $throttler3->setLogger( new NullLogger() );
150
151 $throttled = [ 'throttleIndex' => 0, 'count' => 1, 'wait' => 10 ];
152
153 $result = $throttler1->increase( 'SomeUser', '1.2.3.4' );
154 $this->assertFalse( $result, 'should not throttle' );
155
156 $result = $throttler1->increase( 'SomeUser', '1.2.3.4' );
157 $this->assertEquals( $throttled, $result, 'should throttle' );
158
159 $result = $throttler2->increase( 'SomeUser', '1.2.3.4' );
160 $this->assertEquals( $throttled, $result, 'should throttle, same namespace' );
161
162 $result = $throttler3->increase( 'SomeUser', '1.2.3.4' );
163 $this->assertFalse( $result, 'should not throttle, different namespace' );
164 }
165
166 public function testExpiration() {
167 $cache = $this->getMockBuilder( HashBagOStuff::class )
168 ->setMethods( [ 'add' ] )->getMock();
169 $throttler = new Throttler( [ [ 'count' => 3, 'seconds' => 10 ] ], [ 'cache' => $cache ] );
170 $throttler->setLogger( new NullLogger() );
171
172 $cache->expects( $this->once() )->method( 'add' )->with( $this->anything(), 1, 10 );
173 $throttler->increase( 'SomeUser' );
174 }
175
176 /**
177 * @expectedException \InvalidArgumentException
178 */
179 public function testException() {
180 $throttler = new Throttler( [ [ 'count' => 3, 'seconds' => 10 ] ] );
181 $throttler->setLogger( new NullLogger() );
182 $throttler->increase();
183 }
184
185 public function testLog() {
186 $cache = new \HashBagOStuff();
187 $throttler = new Throttler( [ [ 'count' => 1, 'seconds' => 10 ] ], [ 'cache' => $cache ] );
188
189 $logger = $this->getMockBuilder( AbstractLogger::class )
190 ->setMethods( [ 'log' ] )
191 ->getMockForAbstractClass();
192 $logger->expects( $this->never() )->method( 'log' );
193 $throttler->setLogger( $logger );
194 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
195 $this->assertFalse( $result, 'should not throttle' );
196
197 $logger = $this->getMockBuilder( AbstractLogger::class )
198 ->setMethods( [ 'log' ] )
199 ->getMockForAbstractClass();
200 $logger->expects( $this->once() )->method( 'log' )->with( $this->anything(), $this->anything(), [
201 'throttle' => 'custom',
202 'index' => 0,
203 'ip' => '1.2.3.4',
204 'username' => 'SomeUser',
205 'count' => 1,
206 'expiry' => 10,
207 'method' => 'foo',
208 ] );
209 $throttler->setLogger( $logger );
210 $result = $throttler->increase( 'SomeUser', '1.2.3.4', 'foo' );
211 $this->assertSame( [ 'throttleIndex' => 0, 'count' => 1, 'wait' => 10 ], $result );
212 }
213
214 public function testClear() {
215 $cache = new \HashBagOStuff();
216 $throttler = new Throttler( [ [ 'count' => 1, 'seconds' => 10 ] ], [ 'cache' => $cache ] );
217 $throttler->setLogger( new NullLogger() );
218
219 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
220 $this->assertFalse( $result, 'should not throttle' );
221
222 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
223 $this->assertSame( [ 'throttleIndex' => 0, 'count' => 1, 'wait' => 10 ], $result );
224
225 $result = $throttler->increase( 'OtherUser', '1.2.3.4' );
226 $this->assertFalse( $result, 'should not throttle' );
227
228 $result = $throttler->increase( 'OtherUser', '1.2.3.4' );
229 $this->assertSame( [ 'throttleIndex' => 0, 'count' => 1, 'wait' => 10 ], $result );
230
231 $throttler->clear( 'SomeUser', '1.2.3.4' );
232
233 $result = $throttler->increase( 'SomeUser', '1.2.3.4' );
234 $this->assertFalse( $result, 'should not throttle' );
235
236 $result = $throttler->increase( 'OtherUser', '1.2.3.4' );
237 $this->assertSame( [ 'throttleIndex' => 0, 'count' => 1, 'wait' => 10 ], $result );
238 }
239 }