SettingServiceTest.php 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <?php
  2. namespace Tests\Feature\Services;
  3. use Tests\FeatureTestCase;
  4. use Illuminate\Support\Facades\Crypt;
  5. use Illuminate\Support\Facades\DB;
  6. use App\Models\TwoFAccount;
  7. use App\Services\SettingService;
  8. /**
  9. * @covers \App\Services\SettingService
  10. */
  11. class SettingServiceTest extends FeatureTestCase
  12. {
  13. /**
  14. * App\Services\SettingService $settingService
  15. */
  16. protected $settingService;
  17. /**
  18. * App\Models\Group $groupOne, $groupTwo
  19. */
  20. protected $twofaccountOne, $twofaccountTwo;
  21. private const KEY = 'key';
  22. private const VALUE = 'value';
  23. private const SETTING_NAME = 'MySetting';
  24. private const SETTING_NAME_ALT = 'MySettingAlt';
  25. private const SETTING_VALUE_STRING = 'MyValue';
  26. private const SETTING_VALUE_TRUE_TRANSFORMED = '{{1}}';
  27. private const SETTING_VALUE_FALSE_TRANSFORMED = '{{}}';
  28. private const SETTING_VALUE_INT = 10;
  29. private const ACCOUNT = 'account';
  30. private const SERVICE = 'service';
  31. private const SECRET = 'A4GRFHVVRBGY7UIW';
  32. private const ALGORITHM_CUSTOM = 'sha256';
  33. private const DIGITS_CUSTOM = 7;
  34. private const PERIOD_CUSTOM = 40;
  35. private const IMAGE = 'https%3A%2F%2Fen.opensuse.org%2Fimages%2F4%2F44%2FButton-filled-colour.png';
  36. private const ICON = 'test.png';
  37. private const TOTP_FULL_CUSTOM_URI = 'otpauth://totp/'.self::SERVICE.':'.self::ACCOUNT.'?secret='.self::SECRET.'&issuer='.self::SERVICE.'&digits='.self::DIGITS_CUSTOM.'&period='.self::PERIOD_CUSTOM.'&algorithm='.self::ALGORITHM_CUSTOM.'&image='.self::IMAGE;
  38. /**
  39. * @test
  40. */
  41. public function setUp() : void
  42. {
  43. parent::setUp();
  44. $this->settingService = $this->app->make(SettingService::class);
  45. $this->twofaccountOne = new TwoFAccount;
  46. $this->twofaccountOne->legacy_uri = self::TOTP_FULL_CUSTOM_URI;
  47. $this->twofaccountOne->service = self::SERVICE;
  48. $this->twofaccountOne->account = self::ACCOUNT;
  49. $this->twofaccountOne->icon = self::ICON;
  50. $this->twofaccountOne->otp_type = 'totp';
  51. $this->twofaccountOne->secret = self::SECRET;
  52. $this->twofaccountOne->digits = self::DIGITS_CUSTOM;
  53. $this->twofaccountOne->algorithm = self::ALGORITHM_CUSTOM;
  54. $this->twofaccountOne->period = self::PERIOD_CUSTOM;
  55. $this->twofaccountOne->counter = null;
  56. $this->twofaccountOne->save();
  57. $this->twofaccountTwo = new TwoFAccount;
  58. $this->twofaccountTwo->legacy_uri = self::TOTP_FULL_CUSTOM_URI;
  59. $this->twofaccountTwo->service = self::SERVICE;
  60. $this->twofaccountTwo->account = self::ACCOUNT;
  61. $this->twofaccountTwo->icon = self::ICON;
  62. $this->twofaccountTwo->otp_type = 'totp';
  63. $this->twofaccountTwo->secret = self::SECRET;
  64. $this->twofaccountTwo->digits = self::DIGITS_CUSTOM;
  65. $this->twofaccountTwo->algorithm = self::ALGORITHM_CUSTOM;
  66. $this->twofaccountTwo->period = self::PERIOD_CUSTOM;
  67. $this->twofaccountTwo->counter = null;
  68. $this->twofaccountTwo->save();
  69. }
  70. /**
  71. * @test
  72. */
  73. public function test_get_string_setting_returns_correct_value()
  74. {
  75. $this->settingService->set(self::SETTING_NAME, self::SETTING_VALUE_STRING);
  76. $this->assertEquals(self::SETTING_VALUE_STRING, $this->settingService->get(self::SETTING_NAME));
  77. }
  78. /**
  79. * @test
  80. */
  81. public function test_get_boolean_setting_returns_true()
  82. {
  83. $this->settingService->set(self::SETTING_NAME, self::SETTING_VALUE_TRUE_TRANSFORMED);
  84. $this->assertEquals(true, $this->settingService->get(self::SETTING_NAME));
  85. }
  86. /**
  87. * @test
  88. */
  89. public function test_get_boolean_setting_returns_false()
  90. {
  91. $this->settingService->set(self::SETTING_NAME, self::SETTING_VALUE_FALSE_TRANSFORMED);
  92. $this->assertEquals(false, $this->settingService->get(self::SETTING_NAME));
  93. }
  94. /**
  95. * @test
  96. */
  97. public function test_get_int_setting_returns_int()
  98. {
  99. $this->settingService->set(self::SETTING_NAME, self::SETTING_VALUE_INT);
  100. $value = $this->settingService->get(self::SETTING_NAME);
  101. $this->assertEquals(self::SETTING_VALUE_INT, $value);
  102. $this->assertIsInt($value);
  103. }
  104. /**
  105. * @test
  106. */
  107. public function test_all_returns_native_and_user_settings()
  108. {
  109. $native_options = config('2fauth.options');
  110. $this->settingService->set(self::SETTING_NAME, self::SETTING_VALUE_STRING);
  111. $all = $this->settingService->all();
  112. $this->assertArrayHasKey(self::SETTING_NAME, $all);
  113. $this->assertEquals($all[self::SETTING_NAME], self::SETTING_VALUE_STRING);
  114. foreach ($native_options as $key => $val) {
  115. $this->assertArrayHasKey($key, $all);
  116. $this->assertEquals($all[$key], $val);
  117. }
  118. $this->assertArrayHasKey('lang', $all);
  119. }
  120. /**
  121. * @test
  122. */
  123. public function test_set_setting_persist_correct_value()
  124. {
  125. $value = $this->settingService->set(self::SETTING_NAME, self::SETTING_VALUE_STRING);
  126. $this->assertDatabaseHas('options', [
  127. self::KEY => self::SETTING_NAME,
  128. self::VALUE => self::SETTING_VALUE_STRING
  129. ]);
  130. }
  131. /**
  132. * @test
  133. */
  134. public function test_set_useEncryption_on_encrypts_all_accounts()
  135. {
  136. $this->settingService->set('useEncryption', true);
  137. $twofaccounts = DB::table('twofaccounts')->get();
  138. $twofaccounts->each(function ($item, $key) {
  139. $this->assertEquals(self::ACCOUNT, Crypt::decryptString($item->account));
  140. $this->assertEquals(self::SECRET, Crypt::decryptString($item->secret));
  141. $this->assertEquals(self::TOTP_FULL_CUSTOM_URI, Crypt::decryptString($item->legacy_uri));
  142. });
  143. }
  144. /**
  145. * @test
  146. */
  147. public function test_set_useEncryption_on_twice_prevents_successive_encryption()
  148. {
  149. $this->settingService->set('useEncryption', true);
  150. $this->settingService->set('useEncryption', true);
  151. $twofaccounts = DB::table('twofaccounts')->get();
  152. $twofaccounts->each(function ($item, $key) {
  153. $this->assertEquals(self::ACCOUNT, Crypt::decryptString($item->account));
  154. $this->assertEquals(self::SECRET, Crypt::decryptString($item->secret));
  155. $this->assertEquals(self::TOTP_FULL_CUSTOM_URI, Crypt::decryptString($item->legacy_uri));
  156. });
  157. }
  158. /**
  159. * @test
  160. */
  161. public function test_set_useEncryption_off_decrypts_all_accounts()
  162. {
  163. $this->settingService->set('useEncryption', true);
  164. $this->settingService->set('useEncryption', false);
  165. $twofaccounts = DB::table('twofaccounts')->get();
  166. $twofaccounts->each(function ($item, $key) {
  167. $this->assertEquals(self::ACCOUNT, $item->account);
  168. $this->assertEquals(self::SECRET, $item->secret);
  169. $this->assertEquals(self::TOTP_FULL_CUSTOM_URI, $item->legacy_uri);
  170. });
  171. }
  172. /**
  173. * @test
  174. * @dataProvider provideUndecipherableData
  175. */
  176. public function test_set_useEncryption_off_returns_exception_when_data_are_undecipherable(array $data)
  177. {
  178. $this->expectException(\App\Exceptions\DbEncryptionException::class);
  179. $this->settingService->set('useEncryption', true);
  180. $affected = DB::table('twofaccounts')
  181. ->where('id', $this->twofaccountOne->id)
  182. ->update($data);
  183. $this->settingService->set('useEncryption', false);
  184. $twofaccount = TwoFAccount::find($this->twofaccountOne->id);
  185. }
  186. /**
  187. * Provide invalid data for validation test
  188. */
  189. public function provideUndecipherableData() : array
  190. {
  191. return [
  192. [[
  193. 'account' => 'undecipherableString'
  194. ]],
  195. [[
  196. 'secret' => 'undecipherableString'
  197. ]],
  198. [[
  199. 'legacy_uri' => 'undecipherableString'
  200. ]],
  201. ];
  202. }
  203. /**
  204. * @test
  205. */
  206. public function test_set_array_of_settings_persist_correct_values()
  207. {
  208. $value = $this->settingService->set([
  209. self::SETTING_NAME => self::SETTING_VALUE_STRING,
  210. self::SETTING_NAME_ALT => self::SETTING_VALUE_INT,
  211. ]);
  212. $this->assertDatabaseHas('options', [
  213. self::KEY => self::SETTING_NAME,
  214. self::VALUE => self::SETTING_VALUE_STRING
  215. ]);
  216. $this->assertDatabaseHas('options', [
  217. self::KEY => self::SETTING_NAME_ALT,
  218. self::VALUE => self::SETTING_VALUE_INT
  219. ]);
  220. }
  221. /**
  222. * @test
  223. */
  224. public function test_set_true_setting_persist_transformed_boolean()
  225. {
  226. $value = $this->settingService->set(self::SETTING_NAME, true);
  227. $this->assertDatabaseHas('options', [
  228. self::KEY => self::SETTING_NAME,
  229. self::VALUE => self::SETTING_VALUE_TRUE_TRANSFORMED
  230. ]);
  231. }
  232. /**
  233. * @test
  234. */
  235. public function test_set_false_setting_persist_transformed_boolean()
  236. {
  237. $value = $this->settingService->set(self::SETTING_NAME, false);
  238. $this->assertDatabaseHas('options', [
  239. self::KEY => self::SETTING_NAME,
  240. self::VALUE => self::SETTING_VALUE_FALSE_TRANSFORMED
  241. ]);
  242. }
  243. /**
  244. * @test
  245. */
  246. public function test_del_remove_setting_from_db()
  247. {
  248. DB::table('options')->insert(
  249. [self::KEY => self::SETTING_NAME, self::VALUE => strval(self::SETTING_VALUE_STRING)]
  250. );
  251. $value = $this->settingService->delete(self::SETTING_NAME);
  252. $this->assertDatabaseMissing('options', [
  253. self::KEY => self::SETTING_NAME,
  254. self::VALUE => self::SETTING_VALUE_STRING
  255. ]);
  256. }
  257. }