SettingServiceTest.php 9.9 KB

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