ThrottlerTest.php 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209
  1. <?php
  2. /**
  3. * @copyright Copyright (c) 2016 Lukas Reschke <lukas@statuscode.ch>
  4. *
  5. * @license GNU AGPL version 3 or any later version
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as
  9. * published by the Free Software Foundation, either version 3 of the
  10. * License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. *
  20. */
  21. namespace Test\Security\Bruteforce;
  22. use OC\AppFramework\Utility\TimeFactory;
  23. use OC\Security\Bruteforce\Throttler;
  24. use OCP\IConfig;
  25. use OCP\IDBConnection;
  26. use OCP\ILogger;
  27. use Test\TestCase;
  28. /**
  29. * Based on the unit tests from Paragonie's Airship CMS
  30. * Ref: https://github.com/paragonie/airship/blob/7e5bad7e3c0fbbf324c11f963fd1f80e59762606/test/unit/Engine/Security/AirBrakeTest.php
  31. *
  32. * @package Test\Security\Bruteforce
  33. */
  34. class ThrottlerTest extends TestCase {
  35. /** @var Throttler */
  36. private $throttler;
  37. /** @var IDBConnection */
  38. private $dbConnection;
  39. /** @var ILogger */
  40. private $logger;
  41. /** @var IConfig|\PHPUnit_Framework_MockObject_MockObject */
  42. private $config;
  43. public function setUp() {
  44. $this->dbConnection = $this->createMock(IDBConnection::class);
  45. $this->logger = $this->createMock(ILogger::class);
  46. $this->config = $this->createMock(IConfig::class);
  47. $this->throttler = new Throttler(
  48. $this->dbConnection,
  49. new TimeFactory(),
  50. $this->logger,
  51. $this->config
  52. );
  53. parent::setUp();
  54. }
  55. public function testCutoff() {
  56. // precisely 31 second shy of 12 hours
  57. $cutoff = self::invokePrivate($this->throttler, 'getCutoff', [43169]);
  58. $this->assertSame(0, $cutoff->y);
  59. $this->assertSame(0, $cutoff->m);
  60. $this->assertSame(0, $cutoff->d);
  61. $this->assertSame(11, $cutoff->h);
  62. $this->assertSame(59, $cutoff->i);
  63. $this->assertSame(29, $cutoff->s);
  64. $cutoff = self::invokePrivate($this->throttler, 'getCutoff', [86401]);
  65. $this->assertSame(0, $cutoff->y);
  66. $this->assertSame(0, $cutoff->m);
  67. $this->assertSame(1, $cutoff->d);
  68. $this->assertSame(0, $cutoff->h);
  69. $this->assertSame(0, $cutoff->i);
  70. // Leap second tolerance:
  71. $this->assertLessThan(2, $cutoff->s);
  72. }
  73. public function dataIsIPWhitelisted() {
  74. return [
  75. [
  76. '10.10.10.10',
  77. [
  78. 'whitelist_0' => '10.10.10.0/24',
  79. ],
  80. true,
  81. ],
  82. [
  83. '10.10.10.10',
  84. [
  85. 'whitelist_0' => '192.168.0.0/16',
  86. ],
  87. false,
  88. ],
  89. [
  90. '10.10.10.10',
  91. [
  92. 'whitelist_0' => '192.168.0.0/16',
  93. 'whitelist_1' => '10.10.10.0/24',
  94. ],
  95. true,
  96. ],
  97. [
  98. 'dead:beef:cafe::1',
  99. [
  100. 'whitelist_0' => '192.168.0.0/16',
  101. 'whitelist_1' => '10.10.10.0/24',
  102. 'whitelist_2' => 'deaf:beef:cafe:1234::/64'
  103. ],
  104. false,
  105. ],
  106. [
  107. 'dead:beef:cafe::1',
  108. [
  109. 'whitelist_0' => '192.168.0.0/16',
  110. 'whitelist_1' => '10.10.10.0/24',
  111. 'whitelist_2' => 'deaf:beef::/64'
  112. ],
  113. false,
  114. ],
  115. [
  116. 'dead:beef:cafe::1',
  117. [
  118. 'whitelist_0' => '192.168.0.0/16',
  119. 'whitelist_1' => '10.10.10.0/24',
  120. 'whitelist_2' => 'deaf:cafe::/8'
  121. ],
  122. true,
  123. ],
  124. [
  125. 'invalid',
  126. [],
  127. false,
  128. ],
  129. ];
  130. }
  131. /**
  132. * @param string $ip
  133. * @param string[] $whitelists
  134. * @param bool $isWhiteListed
  135. * @param bool $enabled
  136. */
  137. private function isIpWhiteListedHelper($ip,
  138. $whitelists,
  139. $isWhiteListed,
  140. $enabled) {
  141. $this->config->method('getAppKeys')
  142. ->with($this->equalTo('bruteForce'))
  143. ->willReturn(array_keys($whitelists));
  144. $this->config
  145. ->expects($this->once())
  146. ->method('getSystemValue')
  147. ->with('auth.bruteforce.protection.enabled', true)
  148. ->willReturn($enabled);
  149. $this->config->method('getAppValue')
  150. ->will($this->returnCallback(function($app, $key, $default) use ($whitelists) {
  151. if ($app !== 'bruteForce') {
  152. return $default;
  153. }
  154. if (isset($whitelists[$key])) {
  155. return $whitelists[$key];
  156. }
  157. return $default;
  158. }));
  159. $this->assertSame(
  160. ($enabled === false) ? true : $isWhiteListed,
  161. self::invokePrivate($this->throttler, 'isIPWhitelisted', [$ip])
  162. );
  163. }
  164. /**
  165. * @dataProvider dataIsIPWhitelisted
  166. *
  167. * @param string $ip
  168. * @param string[] $whitelists
  169. * @param bool $isWhiteListed
  170. */
  171. public function testIsIpWhiteListedWithEnabledProtection($ip,
  172. $whitelists,
  173. $isWhiteListed) {
  174. $this->isIpWhiteListedHelper(
  175. $ip,
  176. $whitelists,
  177. $isWhiteListed,
  178. true
  179. );
  180. }
  181. /**
  182. * @dataProvider dataIsIPWhitelisted
  183. *
  184. * @param string $ip
  185. * @param string[] $whitelists
  186. * @param bool $isWhiteListed
  187. */
  188. public function testIsIpWhiteListedWithDisabledProtection($ip,
  189. $whitelists,
  190. $isWhiteListed) {
  191. $this->isIpWhiteListedHelper(
  192. $ip,
  193. $whitelists,
  194. $isWhiteListed,
  195. false
  196. );
  197. }
  198. }