SettingServiceTest.php 9.9 KB

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