You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

BackupCodeStorageTest.php 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237
  1. <?php
  2. /**
  3. * @author Christoph Wurst <christoph@winzerhof-wurst.at>
  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 OCA\TwoFactorBackupCodes\Tests\Unit\Service;
  22. use OCA\TwoFactorBackupCodes\Db\BackupCode;
  23. use OCA\TwoFactorBackupCodes\Db\BackupCodeMapper;
  24. use OCA\TwoFactorBackupCodes\Event\CodesGenerated;
  25. use OCA\TwoFactorBackupCodes\Service\BackupCodeStorage;
  26. use OCP\EventDispatcher\IEventDispatcher;
  27. use OCP\IUser;
  28. use OCP\Security\IHasher;
  29. use OCP\Security\ISecureRandom;
  30. use PHPUnit_Framework_MockObject_MockObject;
  31. use Test\TestCase;
  32. class BackupCodeStorageTest extends TestCase {
  33. /** @var BackupCodeMapper|PHPUnit_Framework_MockObject_MockObject */
  34. private $mapper;
  35. /** @var ISecureRandom|PHPUnit_Framework_MockObject_MockObject */
  36. private $random;
  37. /** @var IHasher|PHPUnit_Framework_MockObject_MockObject */
  38. private $hasher;
  39. /** @var IEventDispatcher|PHPUnit_Framework_MockObject_MockObject */
  40. private $eventDispatcher;
  41. /** @var BackupCodeStorage */
  42. private $storage;
  43. protected function setUp() {
  44. parent::setUp();
  45. $this->mapper = $this->createMock(BackupCodeMapper::class);
  46. $this->random = $this->createMock(ISecureRandom::class);
  47. $this->hasher = $this->createMock(IHasher::class);
  48. $this->eventDispatcher = $this->createMock(IEventDispatcher::class);
  49. $this->storage = new BackupCodeStorage($this->mapper, $this->random, $this->hasher, $this->eventDispatcher);
  50. }
  51. public function testCreateCodes() {
  52. $user = $this->createMock(IUser::class);
  53. $number = 5;
  54. $user->method('getUID')->willReturn('fritz');
  55. $this->random->expects($this->exactly($number))
  56. ->method('generate')
  57. ->with(16, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789')
  58. ->will($this->returnValue('CODEABCDEF'));
  59. $this->hasher->expects($this->exactly($number))
  60. ->method('hash')
  61. ->with('CODEABCDEF')
  62. ->will($this->returnValue('HASHEDCODE'));
  63. $row = new BackupCode();
  64. $row->setUserId('fritz');
  65. $row->setCode('HASHEDCODE');
  66. $row->setUsed(0);
  67. $this->mapper->expects($this->exactly($number))
  68. ->method('insert')
  69. ->with($this->equalTo($row));
  70. $this->eventDispatcher->expects($this->once())
  71. ->method('dispatch')
  72. ->with(
  73. $this->equalTo(CodesGenerated::class),
  74. $this->equalTo(new CodesGenerated($user))
  75. );
  76. $codes = $this->storage->createCodes($user, $number);
  77. $this->assertCount($number, $codes);
  78. foreach ($codes as $code) {
  79. $this->assertEquals('CODEABCDEF', $code);
  80. }
  81. }
  82. public function testHasBackupCodes() {
  83. $user = $this->createMock(IUser::class);
  84. $codes = [
  85. new BackupCode(),
  86. new BackupCode(),
  87. ];
  88. $this->mapper->expects($this->once())
  89. ->method('getBackupCodes')
  90. ->with($user)
  91. ->will($this->returnValue($codes));
  92. $this->assertTrue($this->storage->hasBackupCodes($user));
  93. }
  94. public function testHasBackupCodesNoCodes() {
  95. $user = $this->createMock(IUser::class);
  96. $codes = [];
  97. $this->mapper->expects($this->once())
  98. ->method('getBackupCodes')
  99. ->with($user)
  100. ->will($this->returnValue($codes));
  101. $this->assertFalse($this->storage->hasBackupCodes($user));
  102. }
  103. public function testGetBackupCodeState() {
  104. $user = $this->createMock(IUser::class);
  105. $code1 = new BackupCode();
  106. $code1->setUsed(1);
  107. $code2 = new BackupCode();
  108. $code2->setUsed('0');
  109. $codes = [
  110. $code1,
  111. $code2,
  112. ];
  113. $this->mapper->expects($this->once())
  114. ->method('getBackupCodes')
  115. ->with($user)
  116. ->will($this->returnValue($codes));
  117. $expected = [
  118. 'enabled' => true,
  119. 'total' => 2,
  120. 'used' => 1,
  121. ];
  122. $this->assertEquals($expected, $this->storage->getBackupCodesState($user));
  123. }
  124. public function testGetBackupCodeDisabled() {
  125. $user = $this->createMock(IUser::class);
  126. $codes = [];
  127. $this->mapper->expects($this->once())
  128. ->method('getBackupCodes')
  129. ->with($user)
  130. ->will($this->returnValue($codes));
  131. $expected = [
  132. 'enabled' => false,
  133. 'total' => 0,
  134. 'used' => 0,
  135. ];
  136. $this->assertEquals($expected, $this->storage->getBackupCodesState($user));
  137. }
  138. public function testValidateCode() {
  139. $user = $this->createMock(IUser::class);
  140. $code = new BackupCode();
  141. $code->setUsed(0);
  142. $code->setCode('HASHEDVALUE');
  143. $codes = [
  144. $code,
  145. ];
  146. $this->mapper->expects($this->once())
  147. ->method('getBackupCodes')
  148. ->with($user)
  149. ->will($this->returnValue($codes));
  150. $this->hasher->expects($this->once())
  151. ->method('verify')
  152. ->with('CHALLENGE', 'HASHEDVALUE', $this->anything())
  153. ->will($this->returnValue(true));
  154. $this->mapper->expects($this->once())
  155. ->method('update')
  156. ->with($code);
  157. $this->assertTrue($this->storage->validateCode($user, 'CHALLENGE'));
  158. $this->assertEquals(1, $code->getUsed());
  159. }
  160. public function testValidateUsedCode() {
  161. $user = $this->createMock(IUser::class);
  162. $code = new BackupCode();
  163. $code->setUsed('1');
  164. $code->setCode('HASHEDVALUE');
  165. $codes = [
  166. $code,
  167. ];
  168. $this->mapper->expects($this->once())
  169. ->method('getBackupCodes')
  170. ->with($user)
  171. ->will($this->returnValue($codes));
  172. $this->hasher->expects($this->never())
  173. ->method('verify');
  174. $this->mapper->expects($this->never())
  175. ->method('update');
  176. $this->assertFalse($this->storage->validateCode($user, 'CHALLENGE'));
  177. }
  178. public function testValidateCodeWithWrongHash() {
  179. $user = $this->createMock(IUser::class);
  180. $code = new BackupCode();
  181. $code->setUsed(0);
  182. $code->setCode('HASHEDVALUE');
  183. $codes = [
  184. $code,
  185. ];
  186. $this->mapper->expects($this->once())
  187. ->method('getBackupCodes')
  188. ->with($user)
  189. ->will($this->returnValue($codes));
  190. $this->hasher->expects($this->once())
  191. ->method('verify')
  192. ->with('CHALLENGE', 'HASHEDVALUE')
  193. ->will($this->returnValue(false));
  194. $this->mapper->expects($this->never())
  195. ->method('update');
  196. $this->assertFalse($this->storage->validateCode($user, 'CHALLENGE'));
  197. }
  198. }