SettingServiceTest.php 9.2 KB

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