booth 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (285) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +4 -0
  3. data/LICENSE.md +22 -0
  4. data/README.md +372 -0
  5. data/app/assets/config/booth_manifest.js +15 -0
  6. data/app/assets/images/booth/browsers/README.md +2 -0
  7. data/app/assets/images/booth/browsers/chrome.svg +1 -0
  8. data/app/assets/images/booth/browsers/edge.svg +1 -0
  9. data/app/assets/images/booth/browsers/firefox.svg +1 -0
  10. data/app/assets/images/booth/browsers/internet_explorer.svg +1 -0
  11. data/app/assets/images/booth/browsers/opera.svg +1 -0
  12. data/app/assets/images/booth/browsers/safari.svg +1 -0
  13. data/app/assets/images/booth/browsers/unknown.svg +1 -0
  14. data/app/assets/images/booth/platforms/README.md +2 -0
  15. data/app/assets/images/booth/platforms/android.svg +6 -0
  16. data/app/assets/images/booth/platforms/apple.svg +6 -0
  17. data/app/assets/images/booth/platforms/linux.svg +6 -0
  18. data/app/assets/images/booth/platforms/unknown.svg +1 -0
  19. data/app/assets/images/booth/platforms/windows.svg +6 -0
  20. data/app/assets/javascripts/booth/all.js +162 -0
  21. data/app/assets/javascripts/booth/all.js.map +1 -0
  22. data/app/assets/javascripts/booth/booth.ts +194 -0
  23. data/app/assets/javascripts/booth/webauthn-json.ts +99 -0
  24. data/config/locales/de.yml +84 -0
  25. data/config/locales/en.yml +79 -0
  26. data/lib/booth/adminland/credentials/create.rb +30 -0
  27. data/lib/booth/adminland/onboardings/create.rb +63 -0
  28. data/lib/booth/adminland/onboardings/destroy.rb +50 -0
  29. data/lib/booth/adminland/onboardings/find.rb +93 -0
  30. data/lib/booth/adminland/onboardings/index.rb +23 -0
  31. data/lib/booth/adminland/periodic_cleanup.rb +11 -0
  32. data/lib/booth/adminland/recoveries/consume.rb +70 -0
  33. data/lib/booth/adminland.rb +48 -0
  34. data/lib/booth/audits/register/added_otp.rb +22 -0
  35. data/lib/booth/audits/register/changed_otp.rb +22 -0
  36. data/lib/booth/audits/register/completed_onboarding.rb +22 -0
  37. data/lib/booth/audits/register/correct_otp.rb +42 -0
  38. data/lib/booth/audits/register/correct_password.rb +43 -0
  39. data/lib/booth/audits/register/logout.rb +22 -0
  40. data/lib/booth/audits/register/requested_password_reset.rb +22 -0
  41. data/lib/booth/audits/register/wrong_otp.rb +22 -0
  42. data/lib/booth/audits/register/wrong_password.rb +25 -0
  43. data/lib/booth/authenticators/confirm.rb +34 -0
  44. data/lib/booth/authenticators/credential_mode_after_confirmation.rb +25 -0
  45. data/lib/booth/authenticators/step.rb +19 -0
  46. data/lib/booth/concerns/action.rb +58 -0
  47. data/lib/booth/concerns/transition.rb +17 -0
  48. data/lib/booth/configuration.rb +116 -0
  49. data/lib/booth/configure.rb +37 -0
  50. data/lib/booth/contests/get.rb +36 -0
  51. data/lib/booth/contests/respond.rb +78 -0
  52. data/lib/booth/contests/set_for_login.rb +28 -0
  53. data/lib/booth/cooldowns/distance_of_time.rb +46 -0
  54. data/lib/booth/cooldowns/otp.rb +22 -0
  55. data/lib/booth/cooldowns/password.rb +44 -0
  56. data/lib/booth/cooldowns/password_reset.rb +24 -0
  57. data/lib/booth/cooldowns/strategies/exponential.rb +82 -0
  58. data/lib/booth/cooldowns/strategies/global.rb +62 -0
  59. data/lib/booth/cooldowns/strategies/result.rb +22 -0
  60. data/lib/booth/credentials/create.rb +28 -0
  61. data/lib/booth/credentials/create_with_onboarding.rb +26 -0
  62. data/lib/booth/credentials/find_by_username.rb +45 -0
  63. data/lib/booth/credentials/mode.rb +69 -0
  64. data/lib/booth/credentials/modes/otp_addable.rb +23 -0
  65. data/lib/booth/credentials/modes/otp_changeable.rb +23 -0
  66. data/lib/booth/credentials/modes/otp_manageable.rb +17 -0
  67. data/lib/booth/credentials/modes/otp_removable.rb +23 -0
  68. data/lib/booth/credentials/modes/password_addable.rb +29 -0
  69. data/lib/booth/credentials/modes/password_changeable.rb +31 -0
  70. data/lib/booth/credentials/modes/password_manageable.rb +17 -0
  71. data/lib/booth/credentials/modes/password_removable.rb +24 -0
  72. data/lib/booth/credentials/modes/password_removal_requires_user_verifiable_webauth.rb +16 -0
  73. data/lib/booth/credentials/modes/webauth_addable.rb +26 -0
  74. data/lib/booth/credentials/modes/webauth_manageable.rb +16 -0
  75. data/lib/booth/credentials/modes/webauth_removable.rb +25 -0
  76. data/lib/booth/credentials/otp_authentication.rb +59 -0
  77. data/lib/booth/credentials/password_authentication.rb +72 -0
  78. data/lib/booth/credentials/webauth_challenge.rb +28 -0
  79. data/lib/booth/engine.rb +25 -0
  80. data/lib/booth/errors.rb +86 -0
  81. data/lib/booth/geolocation.rb +20 -0
  82. data/lib/booth/hooks/after_fetch.rb +54 -0
  83. data/lib/booth/hooks/before_logout.rb +29 -0
  84. data/lib/booth/hooks/serialize_from_session.rb +24 -0
  85. data/lib/booth/hooks/serialize_into_session.rb +14 -0
  86. data/lib/booth/logger.rb +41 -0
  87. data/lib/booth/logging.rb +59 -0
  88. data/lib/booth/method_object.rb +73 -0
  89. data/lib/booth/mode.rb +22 -0
  90. data/lib/booth/models/application_record.rb +7 -0
  91. data/lib/booth/models/audit.rb +24 -0
  92. data/lib/booth/models/authenticator.rb +45 -0
  93. data/lib/booth/models/concerns/modeable.rb +50 -0
  94. data/lib/booth/models/concerns/otpable.rb +37 -0
  95. data/lib/booth/models/concerns/passwordable.rb +58 -0
  96. data/lib/booth/models/contest.rb +55 -0
  97. data/lib/booth/models/contests/scopes/recently_created.rb +23 -0
  98. data/lib/booth/models/contests/scopes/recently_responded.rb +32 -0
  99. data/lib/booth/models/credential.rb +61 -0
  100. data/lib/booth/models/onboarding.rb +61 -0
  101. data/lib/booth/models/password_reset.rb +41 -0
  102. data/lib/booth/models/recovery.rb +32 -0
  103. data/lib/booth/models/registration.rb +10 -0
  104. data/lib/booth/models/session.rb +47 -0
  105. data/lib/booth/models/user_agent.rb +50 -0
  106. data/lib/booth/modes/base.rb +25 -0
  107. data/lib/booth/modes/username_and_password.rb +7 -0
  108. data/lib/booth/modes/username_and_webauth.rb +7 -0
  109. data/lib/booth/modes/username_password_and_otp.rb +7 -0
  110. data/lib/booth/modes/username_password_and_webauth.rb +7 -0
  111. data/lib/booth/onboardings/find.rb +35 -0
  112. data/lib/booth/onboardings/propagate_to_credential.rb +63 -0
  113. data/lib/booth/onboardings/step.rb +68 -0
  114. data/lib/booth/password_resets/create.rb +57 -0
  115. data/lib/booth/password_resets/find.rb +36 -0
  116. data/lib/booth/password_resets/propagate_to_credential.rb +36 -0
  117. data/lib/booth/password_resets/step.rb +18 -0
  118. data/lib/booth/recoveries/create.rb +45 -0
  119. data/lib/booth/request.rb +106 -0
  120. data/lib/booth/requests/agent.rb +14 -0
  121. data/lib/booth/requests/authentication.rb +47 -0
  122. data/lib/booth/requests/ip.rb +28 -0
  123. data/lib/booth/requests/return_path.rb +34 -0
  124. data/lib/booth/requests/session.rb +106 -0
  125. data/lib/booth/requests/storage.rb +62 -0
  126. data/lib/booth/requests/storages/login.rb +108 -0
  127. data/lib/booth/requests/storages/otp.rb +54 -0
  128. data/lib/booth/requests/storages/password.rb +49 -0
  129. data/lib/booth/requests/storages/password_reset.rb +35 -0
  130. data/lib/booth/requests/storages/recovery.rb +35 -0
  131. data/lib/booth/requests/storages/registration.rb +27 -0
  132. data/lib/booth/requests/storages/webauth.rb +38 -0
  133. data/lib/booth/requests/sudo.rb +110 -0
  134. data/lib/booth/routes/userland.rb +80 -0
  135. data/lib/booth/sessions/create_and_login.rb +46 -0
  136. data/lib/booth/sessions/historical_locations.rb +18 -0
  137. data/lib/booth/sessions/index.rb +59 -0
  138. data/lib/booth/sessions/revoke.rb +51 -0
  139. data/lib/booth/sessions/revoke_all_others.rb +43 -0
  140. data/lib/booth/sessions/to_passport.rb +51 -0
  141. data/lib/booth/syntaxes/contest_code.rb +58 -0
  142. data/lib/booth/syntaxes/email.rb +97 -0
  143. data/lib/booth/syntaxes/ip.rb +37 -0
  144. data/lib/booth/syntaxes/otp.rb +57 -0
  145. data/lib/booth/syntaxes/scope.rb +21 -0
  146. data/lib/booth/syntaxes/scope_comparison.rb +28 -0
  147. data/lib/booth/syntaxes/secret_key.rb +64 -0
  148. data/lib/booth/syntaxes/username.rb +85 -0
  149. data/lib/booth/syntaxes/uuid.rb +23 -0
  150. data/lib/booth/test/helpers.rb +63 -0
  151. data/lib/booth/test/support/assert_all_partials_were_covered.rb +63 -0
  152. data/lib/booth/test/support/assert_logged_in.rb +49 -0
  153. data/lib/booth/test/support/assert_logged_out.rb +30 -0
  154. data/lib/booth/test/support/assert_partial.rb +29 -0
  155. data/lib/booth/test/support/force_login.rb +26 -0
  156. data/lib/booth/test/support/get_session_value.rb +35 -0
  157. data/lib/booth/test/support/otp_code_from_session.rb +30 -0
  158. data/lib/booth/test/support/soft_reset_session.rb +22 -0
  159. data/lib/booth/test/userland/logins/missing_authenticators.rb +72 -0
  160. data/lib/booth/test/userland/logins/missing_onboarding.rb +35 -0
  161. data/lib/booth/test/userland/logins/username_and_password.rb +40 -0
  162. data/lib/booth/test/userland/logins/username_and_webauth.rb +75 -0
  163. data/lib/booth/test/userland/logins/username_password_and_otp.rb +45 -0
  164. data/lib/booth/test/userland/logins/username_password_and_webauth.rb +86 -0
  165. data/lib/booth/test/userland/onboardings/already_logged_in.rb +64 -0
  166. data/lib/booth/test/userland/onboardings/otp.rb +63 -0
  167. data/lib/booth/test/userland/onboardings/password.rb +49 -0
  168. data/lib/booth/test/userland/onboardings/timeout.rb +47 -0
  169. data/lib/booth/test/userland/otps/manage.rb +86 -0
  170. data/lib/booth/test/userland/password_resets/reset.rb +102 -0
  171. data/lib/booth/test/userland.rb +38 -0
  172. data/lib/booth/test/webauthn/disable.rb +17 -0
  173. data/lib/booth/test/webauthn/enable.rb +19 -0
  174. data/lib/booth/test/webauthn/virtual_authenticators/create.rb +38 -0
  175. data/lib/booth/test/webauthn/virtual_authenticators/destroy.rb +20 -0
  176. data/lib/booth/test.rb +53 -0
  177. data/lib/booth/to_struct.rb +11 -0
  178. data/lib/booth/userland/extract_flash_messages.rb +35 -0
  179. data/lib/booth/userland/logins/create.rb +28 -0
  180. data/lib/booth/userland/logins/destroy.rb +37 -0
  181. data/lib/booth/userland/logins/new.rb +70 -0
  182. data/lib/booth/userland/logins/transitions/create/choose_username.rb +41 -0
  183. data/lib/booth/userland/logins/transitions/create/enter_otp.rb +70 -0
  184. data/lib/booth/userland/logins/transitions/create/skip_remotes.rb +24 -0
  185. data/lib/booth/userland/logins/transitions/create/verify_password.rb +70 -0
  186. data/lib/booth/userland/logins/transitions/create/webauth_authentication_initiation.rb +55 -0
  187. data/lib/booth/userland/logins/transitions/create/webauth_authentication_verification.rb +80 -0
  188. data/lib/booth/userland/logins/transitions/new/already_logged_in.rb +21 -0
  189. data/lib/booth/userland/logins/transitions/new/fallible.rb +27 -0
  190. data/lib/booth/userland/logins/transitions/new/mode_first_time.rb +20 -0
  191. data/lib/booth/userland/logins/transitions/new/mode_username_and_password.rb +20 -0
  192. data/lib/booth/userland/logins/transitions/new/mode_username_and_webauth.rb +26 -0
  193. data/lib/booth/userland/logins/transitions/new/mode_username_password_and_otp.rb +24 -0
  194. data/lib/booth/userland/logins/transitions/new/mode_username_password_and_webauth.rb +24 -0
  195. data/lib/booth/userland/logins/transitions/new/no_username_chosen.rb +19 -0
  196. data/lib/booth/userland/logins/transitions/new/remote_session_available.rb +52 -0
  197. data/lib/booth/userland/logins/transitions/new/timed_out.rb +25 -0
  198. data/lib/booth/userland/onboardings/show.rb +74 -0
  199. data/lib/booth/userland/onboardings/transitions/update/choose_mode.rb +58 -0
  200. data/lib/booth/userland/onboardings/transitions/update/choose_password.rb +41 -0
  201. data/lib/booth/userland/onboardings/transitions/update/choose_webauth_nickname.rb +50 -0
  202. data/lib/booth/userland/onboardings/transitions/update/confirm_otp.rb +58 -0
  203. data/lib/booth/userland/onboardings/transitions/update/confirm_password.rb +49 -0
  204. data/lib/booth/userland/onboardings/transitions/update/register_otp.rb +31 -0
  205. data/lib/booth/userland/onboardings/transitions/update/reset_otp.rb +40 -0
  206. data/lib/booth/userland/onboardings/transitions/update/reset_password.rb +35 -0
  207. data/lib/booth/userland/onboardings/transitions/update/reset_webauth.rb +46 -0
  208. data/lib/booth/userland/onboardings/transitions/update/webauth_authentication_initiation.rb +40 -0
  209. data/lib/booth/userland/onboardings/transitions/update/webauth_authentication_verification.rb +59 -0
  210. data/lib/booth/userland/onboardings/transitions/update/webauth_registration_initiation.rb +46 -0
  211. data/lib/booth/userland/onboardings/transitions/update/webauth_registration_verification.rb +56 -0
  212. data/lib/booth/userland/onboardings/update.rb +68 -0
  213. data/lib/booth/userland/otps/destroy.rb +42 -0
  214. data/lib/booth/userland/otps/edit.rb +72 -0
  215. data/lib/booth/userland/otps/guards/manageable.rb +21 -0
  216. data/lib/booth/userland/otps/guards/sudo.rb +23 -0
  217. data/lib/booth/userland/otps/show.rb +36 -0
  218. data/lib/booth/userland/otps/sudo.rb +51 -0
  219. data/lib/booth/userland/otps/transitions/update/confirm.rb +84 -0
  220. data/lib/booth/userland/otps/transitions/update/register.rb +40 -0
  221. data/lib/booth/userland/otps/transitions/update/reset.rb +31 -0
  222. data/lib/booth/userland/otps/update.rb +34 -0
  223. data/lib/booth/userland/password_resets/create.rb +73 -0
  224. data/lib/booth/userland/password_resets/guards/logged_out.rb +21 -0
  225. data/lib/booth/userland/password_resets/new.rb +57 -0
  226. data/lib/booth/userland/password_resets/show.rb +77 -0
  227. data/lib/booth/userland/password_resets/transitions/update/choose_password.rb +48 -0
  228. data/lib/booth/userland/password_resets/transitions/update/confirm_password.rb +54 -0
  229. data/lib/booth/userland/password_resets/transitions/update/reset_password.rb +29 -0
  230. data/lib/booth/userland/password_resets/update.rb +65 -0
  231. data/lib/booth/userland/passwords/destroy.rb +41 -0
  232. data/lib/booth/userland/passwords/edit.rb +54 -0
  233. data/lib/booth/userland/passwords/guards/manageable.rb +21 -0
  234. data/lib/booth/userland/passwords/guards/removable.rb +21 -0
  235. data/lib/booth/userland/passwords/guards/sudo.rb +21 -0
  236. data/lib/booth/userland/passwords/remove.rb +34 -0
  237. data/lib/booth/userland/passwords/show.rb +32 -0
  238. data/lib/booth/userland/passwords/sudo.rb +55 -0
  239. data/lib/booth/userland/passwords/transitions/remove/step.rb +27 -0
  240. data/lib/booth/userland/passwords/transitions/update/choose_password.rb +62 -0
  241. data/lib/booth/userland/passwords/transitions/update/confirm_password.rb +82 -0
  242. data/lib/booth/userland/passwords/update.rb +33 -0
  243. data/lib/booth/userland/personal_contests/show.rb +60 -0
  244. data/lib/booth/userland/personal_contests/update.rb +37 -0
  245. data/lib/booth/userland/recoveries/create.rb +48 -0
  246. data/lib/booth/userland/recoveries/new.rb +35 -0
  247. data/lib/booth/userland/registrations/create.rb +56 -0
  248. data/lib/booth/userland/registrations/new.rb +39 -0
  249. data/lib/booth/userland/sessions/destroy_one_or_other.rb +41 -0
  250. data/lib/booth/userland/sessions/index.rb +27 -0
  251. data/lib/booth/userland/sessions/show.rb +31 -0
  252. data/lib/booth/userland/sessions/transitions/destroy/enter_password.rb +50 -0
  253. data/lib/booth/userland/sessions/transitions/destroy/enter_webauth.rb +56 -0
  254. data/lib/booth/userland/sessions/transitions/destroy/verify_password.rb +83 -0
  255. data/lib/booth/userland/sessions/transitions/destroy/webauth_authentication_initiation.rb +38 -0
  256. data/lib/booth/userland/sessions/transitions/destroy/webauth_authentication_verification.rb +61 -0
  257. data/lib/booth/userland/sessions/transitions/show/enter_webauth.rb +56 -0
  258. data/lib/booth/userland/webauths/create.rb +83 -0
  259. data/lib/booth/userland/webauths/destroy.rb +60 -0
  260. data/lib/booth/userland/webauths/guards/manageable.rb +21 -0
  261. data/lib/booth/userland/webauths/guards/sudo.rb +22 -0
  262. data/lib/booth/userland/webauths/index.rb +43 -0
  263. data/lib/booth/userland/webauths/new.rb +70 -0
  264. data/lib/booth/userland/webauths/sudo.rb +25 -0
  265. data/lib/booth/userland/webauths/transitions/create/authentication_initiation.rb +52 -0
  266. data/lib/booth/userland/webauths/transitions/create/authentication_verification.rb +64 -0
  267. data/lib/booth/userland/webauths/transitions/create/choose_nickname.rb +50 -0
  268. data/lib/booth/userland/webauths/transitions/create/registration_initiation.rb +61 -0
  269. data/lib/booth/userland/webauths/transitions/create/registration_verification.rb +68 -0
  270. data/lib/booth/userland/webauths/transitions/create/reset.rb +36 -0
  271. data/lib/booth/userland/webauths/transitions/new/step.rb +23 -0
  272. data/lib/booth/userland/webauths/transitions/sudo/authentication_initiation.rb +47 -0
  273. data/lib/booth/userland/webauths/transitions/sudo/authentication_verification.rb +34 -0
  274. data/lib/booth/userland.rb +192 -0
  275. data/lib/booth/version.rb +3 -0
  276. data/lib/booth/webauth/authentication_verification.rb +68 -0
  277. data/lib/booth/webauth/demand_user_verification.rb +29 -0
  278. data/lib/booth/webauth/options_for_create.rb +46 -0
  279. data/lib/booth/webauth/options_for_get.rb +29 -0
  280. data/lib/booth.rb +267 -0
  281. data/lib/generators/booth/migration/migration_generator.rb +25 -0
  282. data/lib/generators/booth/migration/templates/add_credential_to_users.erb +18 -0
  283. data/lib/generators/booth/migration/templates/create_booth_mode_types.erb +20 -0
  284. data/lib/generators/booth/migration/templates/create_booth_tables.erb +135 -0
  285. metadata +861 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 277fda68ddc0994bf94d53a2c403eed2f8a4b7222df9e517030e4c54118104c6
4
+ data.tar.gz: 3374a71fe3d15ec5896cb8ee645ab9f509f69d3ede4755d9b03d66e5b5c9db5f
5
+ SHA512:
6
+ metadata.gz: dc0575e08fa20689ebe590d42a0347b86c880176ea0420f17b0a7de4b1ad8693e998d4886c16c4197ce570376172fc66c2fcf55508923e71b58cc8e65639d025
7
+ data.tar.gz: 2c226f46885aee67f171c7661be1e8493513c6f8771c46fcaf2163bd31414734342c21fd5a4c9b8f61144a32d6563ac4c6cd8609c72d174b38380f218ed12ab9
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ # 0.0.1
2
+
3
+ First publication for experimentation.
4
+ Test coverage is pretty good and the code is rather safe - but use with caution.
data/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2019-2022 halo https://github.com/halo/booth
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,372 @@
1
+
2
+
3
+ # Setup DB and run tests
4
+
5
+ ```sh
6
+ rake
7
+ ```
8
+
9
+ Run tests inside rails support app:
10
+
11
+ ```sh
12
+ cd test/dummy
13
+ bin/rails test:system
14
+
15
+ # Run all developer integration tests
16
+ bin/rails test
17
+
18
+ # Run one developer integration test
19
+ bin/rails test --name test_booth_test_userland_otps_manage
20
+ ```
21
+
22
+ Debugging webauthn: log/crmux.log
23
+
24
+ ---
25
+
26
+ # Generate documentation
27
+
28
+ ```sh
29
+ gem install yardoc
30
+ yard server --reload
31
+ ```
32
+
33
+ # Public API
34
+
35
+ ```ruby
36
+ # As long as you only call methods in these modules,
37
+ # you are using the supported API of Booth.
38
+ Booth.some_method
39
+ Booth::Userland.some_method
40
+ Booth::Adminland.some_method
41
+ ```
42
+
43
+ When testing you may use this
44
+
45
+ ```ruby
46
+ require 'booth/test'
47
+ # If you call methods in this module, you're using the official API.
48
+ Booth::Test.some_method
49
+ ```
50
+
51
+ # Brainstorming data models
52
+
53
+
54
+ User (email) (you own that)
55
+
56
+ Things that can initiate a session:
57
+
58
+ (A) Credential (username/password)
59
+ (B) Registration (name, email)
60
+ (C) Incognito User
61
+
62
+ Passport (session, belongs to A, B or C)
63
+
64
+
65
+ username
66
+
67
+ either PASS or WEBAUTH
68
+
69
+ Pass -> unknown username or password
70
+ webauth -> fake challenge
71
+
72
+
73
+ # questions
74
+
75
+ PRO USERNAME
76
+ - enumeration-safe at login
77
+ - can register as guest without immediate email access
78
+
79
+ PRO EMAIL
80
+ - prevent enumeration
81
+
82
+
83
+ - usernames or email addresses?
84
+ - enumeration?
85
+
86
+ usernames: enumeration not such a problem
87
+ email addresses: can recover, but inconvenient login VS enumeration, convenient registration anyway
88
+ password guessing on admin login: of course people know our personal employee email addresses!
89
+
90
+ User Enumeration:
91
+
92
+
93
+ ---
94
+
95
+ # Assumptions
96
+
97
+ There may be multiple *TABLES* of users (e.g. customers and employees)
98
+
99
+ You can either use integers or UUIDs as primary keys
100
+
101
+ OTP is optional
102
+
103
+ Employees can incognito login as users and other employees
104
+
105
+ # Incentives to use 2FA
106
+
107
+ - password-only has global blocking (hacker may lock you out)
108
+ - OTP has exponential blocking on IP-range and time (colleague may lock you out)
109
+ - Webauth has no blocking (nobody can lock you out)
110
+
111
+ # Possible improvements
112
+
113
+ - Add an ephemeral credential ID to the password reset token.
114
+ So when somebody tries to brute-force tokens, we can block per user, i.e. protecting all other users.
115
+ But the tokens only live 2 hours, not enough time to brute force anything.
116
+ I'm opening up for a DOS where the hacker locks out the user's password reset attempts
117
+ Unless I make the credential ID ephemeral.
118
+ But then it is just as likely that the hacker guesses the ephemeral ID as well, and then the token.
119
+ And it adds complexity. A single token is really not complex.
120
+ See https://security.stackexchange.com/a/99990 and https://github.com/jeremyevans/rodauth#label-Tokens
121
+
122
+ - I could one-way hash the reset token and send the base to the user
123
+ That way, if the database is leaked, no token in the DB can be used to reset anything (it's just digests).
124
+ But then there is the change of digest collision if the digest doesn't map 1-1 to the original key length.
125
+ Maybe it's better just to rely on the 2 hours validity of each password reset string.
126
+ I think it's more likely somebody get's access to the original reset link in transmit (email provider, IT administrator, etc.)
127
+ If you find a dump of our production database, it's likely older than 2 hours.
128
+ And if you have direct access to our database, then why would you even try to reset a user account?
129
+ You already have all user data.
130
+
131
+ # Use Cases
132
+
133
+ Given an email address, send an invitiation link
134
+ Given an email address
135
+
136
+
137
+ ### remote login assumptions:
138
+ a) non-trivial activity required on part of the remote session owner
139
+ (i.e. don't just click to log in the new browser, but enter a code challenge-response or something)
140
+ b) no guessing of the challenge by the new browser alone
141
+ (i.e. don't just ask for a 9-digit response-number and that would immediately log you in)
142
+
143
+
144
+ ### Log out all sessions after changing password?
145
+
146
+ ## Let a new user choose password and OTP
147
+ ## Let a new user choose password and FIDO2
148
+
149
+
150
+ ## Change the onboarding email address
151
+ -> no, because we cannot check for duplicates *during* onboarding
152
+
153
+
154
+ ## Check status of user onboarding
155
+ ## Prevent hackers from guessing the password or the OTP via login brute-force
156
+
157
+ ## Login with username/password, then see safety phrase, need to enter OTP
158
+ ## Login with username/password, then see safety phrase, logged in
159
+ ## Inform about missing cookie permissions
160
+ ## Maintain a server-side session
161
+ ## Logout this session
162
+ ## Logout all other sessions
163
+
164
+ ## Change your own login username
165
+ ## Change your own password (and logout all other sessions)
166
+ ## Change your own OTP
167
+ ## Add another OTP device (i.e. show OTP) needs: session
168
+
169
+ ## Can have same username in different scopes
170
+
171
+ ## Recover your own forgotten password
172
+
173
+ ## Admin allows you to choose new password
174
+ ## Admin allows you to choose new password and OTP
175
+ ## Admin allows you to choose new OTP
176
+
177
+ ## Assign another scope to a user
178
+
179
+
180
+
181
+ # Usage
182
+
183
+ ---
184
+
185
+ ## Let a new user choose password, OTP and safety phrase
186
+
187
+ ### Create new credential
188
+
189
+ If you want one of your users to login, you need to attach a credential record to them.
190
+ The user will not be able to login at that time, because the password is random and unguessable.
191
+
192
+ You need to specify a (warden) scope where this credential is valid. E.g. :user, :admin, :customer
193
+
194
+ This method will create orphan credentials if called needlessly.
195
+ This is because you fully own the reference to the credential in your business domain.
196
+ It is your job to clean up orphan credentials (TODO: how?)
197
+
198
+ ```ruby
199
+ INPUT: NONE
200
+ OUTPUT: UUID of the created credential
201
+
202
+ id = Booth::Credentials.create scope: :admin
203
+ #=> 'cbc140eb-1f14-4582-a33f-5b0067e10629'
204
+
205
+ MyUser.create credential_id: id
206
+ ```
207
+
208
+ ### Generate Onboarding
209
+
210
+
211
+
212
+ ---
213
+
214
+
215
+
216
+ ## Initiate onboarding
217
+
218
+
219
+
220
+ ## Generate link to choose new Password
221
+
222
+
223
+ ## Generate link to choose new OTP
224
+
225
+
226
+
227
+ # Security Documentation
228
+
229
+ ## Strategies for blocking further attempts after wrong password (or OTP)
230
+
231
+
232
+
233
+ ### Global block by number of attempts
234
+
235
+ TL;DR: Preferred for password-only credentials.
236
+
237
+ #### PRO
238
+
239
+ * The only way to prevent password iteration given a known username.
240
+ * It is simple and user-friendly to hide the password input-field if the limit has been reached.
241
+
242
+ #### CON
243
+
244
+ a) User can be locked out if hacker knows username.
245
+ b) Hacker can know when block was lifted by customer support.
246
+ c) Hacker can know at which point the user logged in (by observing change in the limit).
247
+
248
+ #### Mitigations
249
+
250
+ a) It is better for the (password-only) user to be locked out (and contact customer service) than to be exposed.
251
+ b) Lifting the block is to be combined with changing the username.
252
+ c) None. This is the major drawback of this strategy. But we already reveal whether a user is logged in or not. Also, when only WebAuthn is allowed, there is no limit and thus a particular user group (e.g. employee login) can be protected from leaking information by enforcing WebAuthn.
253
+
254
+ #### Comments
255
+
256
+ The pros and cons also hold true for a global exponential back-off or blocking per time interval. So there is no real advantage of those two variants over a global block by number of attempts.
257
+
258
+
259
+
260
+ ### IP-based exponential back-off block
261
+
262
+ TL;DR: Preferred for password-with-OTP-credentials.
263
+
264
+ #### PRO
265
+
266
+ * User cannot be locked out from outside LAN if hacker knows username.
267
+ * Hacker slowed down because more IPs are needed for password iteration.
268
+ * User can be unblocked without unblocking the hacker.
269
+
270
+ #### CON
271
+
272
+ * User can be locked out by a hacker in LAN.
273
+ * Hacker in LAN can know when block was lifted.
274
+ * Hacker in LAN can know when user logged in (by observing change in the limit).
275
+ * Not really a hindrance to password iteration because many different IPs are available.
276
+ * Effectively allows for password iteration of known usernames as long as IPs are available.
277
+
278
+ #### Mitigations
279
+
280
+ * Do not limit passwordless logins (hardware is secure), limit OTP exponentially on /24-IP (to hinder slow-iteration) and only-password globally (to prevent iteration altogether).
281
+ * Report when too many failed attempts were detected, regardless of IP.
282
+ * Urge users to move to passwordless login (or at least 2FA).
283
+
284
+ * On successful login unblock only exact IP
285
+ * If exact user IP is known, lift the block of only an exact IP
286
+ * If user IP is not known, change the username and then lift the block on all attempts
287
+
288
+
289
+ ### No block at all
290
+
291
+ TL;DR: Preferred for WebAuthn-credentials.
292
+
293
+ Reporting should still be in place, though, just as with password-and-OTP-blocks.
294
+
295
+
296
+
297
+
298
+ ## Things we intentionally reveal about the user with the username alice:
299
+
300
+
301
+ ### Did the user alice set an authentication mode yet (via onboarding?
302
+
303
+
304
+
305
+
306
+
307
+ ### Is the username Alice registered?
308
+
309
+ #### What is the user benefit?
310
+
311
+ #### How could an attacker abuse this?
312
+
313
+ #### Mitigations
314
+
315
+ #### Comments
316
+
317
+ We allow passwordless authentication via WebAuthn. That is somewhat problematic to implement when at the same time [trying to avoid](https://www.twelve21.io/user-enumeration-in-webauthn) user enumeration.
318
+
319
+
320
+
321
+
322
+
323
+ ### Does the user Alice log in passwordlessly via WebAuthn?
324
+
325
+ #### What is the user benefit?
326
+
327
+ Most users won't even know what a WebAuthn device is. It would be confusing to let the user at login choose between entering a password or a hardware token. After a few months not using our service, the user might have forgotten whether a password or a hardware token was used during registration.
328
+
329
+ #### How could an attacker abuse this?
330
+
331
+ Knowing that Alice uses only a hardware key to authenticate, an attacker might be encouraged to steal the hardware key.
332
+
333
+ #### Mitigations
334
+
335
+ The risk of Alice physically loosing the hardware key is comparable to the risk of an attacker physically stealing the hardware key. We accept that risk. The possession of a hardware key is still considered safer than passwords.
336
+
337
+ #### Comments
338
+
339
+ An attacker knowing that Alice does *not* login passwordlessly is unproblematic. Because the password might still be combined with with 2FA.
340
+
341
+ We do not rely on [Resident Keys](https://developers.yubico.com/WebAuthn/WebAuthn_Developer_Guide/Resident_Keys.html) to protect the user's privacy. But we always demand [user verification](https://developers.yubico.com/WebAuthn/WebAuthn_Developer_Guide/User_Presence_vs_User_Verification.html) for passwordless logins.
342
+
343
+
344
+
345
+
346
+
347
+
348
+ ### Is the user Alice logged in right now?
349
+
350
+ #### What is the user benefit?
351
+
352
+ Users experience frustration when they take up one of their many devices (phone, laptop, tablet) and they would like to use our services, but they are not logged in. The user is presented with a login prompt. Possibly in a hurry or without access to the login credentials (forgot password, no computer at hand, no password manager at hand).
353
+
354
+ In order to make it easier for the user to log in without any credentials in such a situation, we detect whether the user is already logged in on another, existing device. We then present instructions that instruct the user on how to authenticate the new device via the existing session on the existing device.
355
+
356
+ #### How could an attacker abuse this?
357
+
358
+ Once an attacker knows that Alice is a registered username, the attacker can naturally assume that Alice is logged in right now on some device. Yet by providing certainty about the user's login state, we provide the attacker with a time window of when it would be effective to launch an attack.
359
+
360
+ Suppose the attacker notices that Alice just logged in (for the first time) or that Alice just logged out (of the last active device). The attacker could then contact Alice, impersonate our employees, and gain trust by providing information about the session state of Alice's devices.
361
+
362
+ #### Mitigations
363
+
364
+ When a user attempts to log in on a new device we could simply always show the instructions of how to log in using another device (regardless of whether another device actually exists or not).
365
+
366
+ However, we would like to give the user certainty that there is no other way to log in right now, except by entering the credentials, when we ask for the credentials rather than showing instructions.
367
+
368
+ As for the impersonation, we are willing to take that risk. At the least, the attacker would have to know how to contact Alice. That is only possible for self-explanatory usernames (such as email addresses or publicly known unique names).
369
+
370
+ #### Comments
371
+
372
+ One could also signal on the logged in device that someone would like to log in right now (like Apple does it among their devices). But that's not feasible for a website. Nor do we want to open an attack vector where the user is alerted when an attacker merely tries to login, because some users will simply press OK.
@@ -0,0 +1,15 @@
1
+ // This file was generated using a rake task
2
+
3
+ //= link booth/browsers/chrome.svg
4
+ //= link booth/browsers/edge.svg
5
+ //= link booth/browsers/firefox.svg
6
+ //= link booth/browsers/opera.svg
7
+ //= link booth/browsers/safari.svg
8
+ //= link booth/browsers/internet_explorer.svg
9
+ //= link booth/browsers/unknown.svg
10
+
11
+ //= link booth/platforms/android.svg
12
+ //= link booth/platforms/apple.svg
13
+ //= link booth/platforms/linux.svg
14
+ //= link booth/platforms/windows.svg
15
+ //= link booth/platforms/unknown.svg
@@ -0,0 +1,2 @@
1
+ Downloaded from https://github.com/alrra/browser-logos
2
+ Update with `rake download_logos`
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -10 276 276"><linearGradient id="a" x1="145" x2="34" y1="253" y2="61" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#1e8e3e"/><stop offset="1" stop-color="#34a853"/></linearGradient><linearGradient id="b" x1="111" x2="222" y1="254" y2="62" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#fcc934"/><stop offset="1" stop-color="#fbbc04"/></linearGradient><linearGradient id="c" x1="17" x2="239" y1="80" y2="80" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#d93025"/><stop offset="1" stop-color="#ea4335"/></linearGradient><circle cx="128" cy="128" r="64" fill="#fff"/><path fill="url(#a)" d="M96 183.4A63.7 63.7 0 0 1 72.6 160L17.2 64A128 128 0 0 0 128 256l55.4-96A64 64 0 0 1 96 183.4Z"/><path fill="url(#b)" d="M192 128a63.7 63.7 0 0 1-8.6 32L128 256A128 128 0 0 0 238.9 64h-111a64 64 0 0 1 64 64Z"/><circle cx="128" cy="128" r="52" fill="#1a73e8"/><path fill="url(#c)" d="M96 72.6a63.7 63.7 0 0 1 32-8.6h110.8a128 128 0 0 0-221.7 0l55.5 96A64 64 0 0 1 96 72.6Z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 27600 27600"><linearGradient id="A" gradientUnits="userSpaceOnUse"/><linearGradient id="B" x1="6870" x2="24704" y1="18705" y2="18705" xlink:href="#A"><stop offset="0" stop-color="#0c59a4"/><stop offset="1" stop-color="#114a8b"/></linearGradient><linearGradient id="C" x1="16272" x2="5133" y1="10968" y2="23102" xlink:href="#A"><stop offset="0" stop-color="#1b9de2"/><stop offset=".16" stop-color="#1595df"/><stop offset=".67" stop-color="#0680d7"/><stop offset="1" stop-color="#0078d4"/></linearGradient><radialGradient id="D" cx="16720" cy="18747" r="9538" xlink:href="#A"><stop offset=".72" stop-opacity="0"/><stop offset=".95" stop-opacity=".53"/><stop offset="1"/></radialGradient><radialGradient id="E" cx="7130" cy="19866" r="14324" gradientTransform="matrix(.14843 -.98892 .79688 .1196 -8759 25542)" xlink:href="#A"><stop offset=".76" stop-opacity="0"/><stop offset=".95" stop-opacity=".5"/><stop offset="1"/></radialGradient><radialGradient id="F" cx="2523" cy="4680" r="20243" gradientTransform="matrix(-.03715 .99931 -2.12836 -.07913 13579 3530)" xlink:href="#A"><stop offset="0" stop-color="#35c1f1"/><stop offset=".11" stop-color="#34c1ed"/><stop offset=".23" stop-color="#2fc2df"/><stop offset=".31" stop-color="#2bc3d2"/><stop offset=".67" stop-color="#36c752"/></radialGradient><radialGradient id="G" cx="24247" cy="7758" r="9734" gradientTransform="matrix(.28109 .95968 -.78353 .22949 24510 -16292)" xlink:href="#A"><stop offset="0" stop-color="#66eb6e"/><stop offset="1" stop-color="#66eb6e" stop-opacity="0"/></radialGradient><path id="H" d="M24105 20053a9345 9345 0 01-1053 472 10202 10202 0 01-3590 646c-4732 0-8855-3255-8855-7432 0-1175 680-2193 1643-2729-4280 180-5380 4640-5380 7253 0 7387 6810 8137 8276 8137 791 0 1984-230 2704-456l130-44a12834 12834 0 006660-5282c220-350-168-757-535-565z"/><path id="I" d="M11571 25141a7913 7913 0 01-2273-2137 8145 8145 0 01-1514-4740 8093 8093 0 013093-6395 8082 8082 0 011373-859c312-148 846-414 1554-404a3236 3236 0 012569 1297 3184 3184 0 01636 1866c0-21 2446-7960-8005-7960-4390 0-8004 4166-8004 7820 0 2319 538 4170 1212 5604a12833 12833 0 007684 6757 12795 12795 0 003908 610c1414 0 2774-233 4045-656a7575 7575 0 01-6278-803z"/><path id="J" d="M16231 15886c-80 105-330 250-330 566 0 260 170 512 472 723 1438 1003 4149 868 4156 868a5954 5954 0 003027-839 6147 6147 0 001133-850 6180 6180 0 001910-4437c26-2242-796-3732-1133-4392-2120-4141-6694-6525-11668-6525-7011 0-12703 5635-12798 12620 47-3654 3679-6605 7996-6605 350 0 2346 34 4200 1007 1634 858 2490 1894 3086 2921 618 1067 728 2415 728 2952s-271 1333-780 1990z"/><use fill="url(#B)" xlink:href="#H"/><use fill="url(#D)" opacity=".35" xlink:href="#H"/><use fill="url(#C)" xlink:href="#I"/><use fill="url(#E)" opacity=".4" xlink:href="#I"/><use fill="url(#F)" xlink:href="#J"/><use fill="url(#G)" xlink:href="#J"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 51500 51500"><radialGradient id="b" cx="87.4%" cy="-12.9%" r="128%" gradientTransform="matrix(.8 0 0 1 .18 .13)"><stop offset=".13" stop-color="#ffbd4f"/><stop offset=".28" stop-color="#ff980e"/><stop offset=".47" stop-color="#ff3750"/><stop offset=".78" stop-color="#eb0878"/><stop offset=".86" stop-color="#e50080"/></radialGradient><radialGradient id="d" cx="49%" cy="40%" r="128%" gradientTransform="matrix(.82 0 0 1 .09 0)"><stop offset=".3" stop-color="#960e18"/><stop offset=".35" stop-color="#b11927" stop-opacity=".74"/><stop offset=".43" stop-color="#db293d" stop-opacity=".34"/><stop offset=".5" stop-color="#f5334b" stop-opacity=".1"/><stop offset=".53" stop-color="#ff3750" stop-opacity="0"/></radialGradient><radialGradient id="e" cx="48%" cy="-12%" r="140%"><stop offset=".13" stop-color="#fff44f"/><stop offset=".53" stop-color="#ff980e"/></radialGradient><radialGradient id="f" cx="22.76%" cy="110.11%" r="100%"><stop offset=".35" stop-color="#3a8ee6"/><stop offset=".67" stop-color="#9059ff"/><stop offset="1" stop-color="#c139e6"/></radialGradient><radialGradient id="h" cx="52%" cy="33%" r="59%" gradientTransform="scale(.9 1)"><stop offset=".21" stop-color="#9059ff" stop-opacity="0"/><stop offset=".97" stop-color="#6e008b" stop-opacity=".6"/></radialGradient><radialGradient id="i" cx="210%" cy="-100%" r="290%"><stop offset=".1" stop-color="#ffe226"/><stop offset=".79" stop-color="#ff7139"/></radialGradient><radialGradient id="j" cx="84%" cy="-41%" r="180%"><stop offset=".11" stop-color="#fff44f"/><stop offset=".46" stop-color="#ff980e"/><stop offset=".72" stop-color="#ff3647"/><stop offset=".9" stop-color="#e31587"/></radialGradient><radialGradient id="k" cx="16.1%" cy="-18.6%" r="348.8%" gradientTransform="matrix(.10453 .46743 -.99452 .04913 -.05 -.26)"><stop offset="0" stop-color="#fff44f"/><stop offset=".3" stop-color="#ff980e"/><stop offset=".57" stop-color="#ff3647"/><stop offset=".74" stop-color="#e31587"/></radialGradient><radialGradient id="l" cx="18.9%" cy="-42.5%" r="238.4%"><stop offset=".14" stop-color="#fff44f"/><stop offset=".48" stop-color="#ff980e"/><stop offset=".66" stop-color="#ff3647"/><stop offset=".9" stop-color="#e31587"/></radialGradient><radialGradient id="m" cx="159.3%" cy="-44.72%" r="313.1%"><stop offset=".09" stop-color="#fff44f"/><stop offset=".63" stop-color="#ff980e"/></radialGradient><linearGradient id="a" x1="87.25%" x2="9.4%" y1="15.5%" y2="93.1%"><stop offset=".05" stop-color="#fff44f"/><stop offset=".37" stop-color="#ff980e"/><stop offset=".53" stop-color="#ff3647"/><stop offset=".7" stop-color="#e31587"/></linearGradient><linearGradient id="n" x1="80%" x2="18%" y1="14%" y2="84%"><stop offset=".17" stop-color="#fff44f" stop-opacity=".8"/><stop offset=".6" stop-color="#fff44f" stop-opacity="0"/></linearGradient><path id="c" d="M47870 16735c-1044-2512-3160-5224-4820-6082 1352 2650 2134 5310 2433 7294 0-6 2 5 4 22l4 26c2268 6147 1032 12398-748 16218-2754 5910-9420 11967-19857 11670-11276-318-21210-8683-23064-19643-338-1728 0-2605 170-4008-207 1080-286 1394-390 3315l-2 123c0 13270 10760 24030 24032 24030 11887 0 21756-8630 23690-19963l110-927c477-4120-53-8453-1560-12075z"/><path id="g" d="M25677 21050c-40 598-2150 2660-2890 2660-6834 0-7943 4133-7943 4133 303 3480 2726 6348 5660 7865 134 70 270 130 405 193a13277 13277 0 00706 289 10674 10674 0 003127 603c11978 562 14300-14320 5655-18640 2213-385 4510 505 5794 1407-2100-3672-6025-6150-10530-6150-285 0-564 24-844 43a12025 12025 0 00-6614 2549c366 310 780 724 1650 1583 1630 1606 5813 3270 5822 3465z"/><path fill="url(#a)" d="M47870 16735c-1044-2512-3160-5224-4820-6082 1352 2650 2134 5310 2433 7294l5 40c-2718-6773-7325-9505-11088-15452l-566-920a7372 7372 0 01-265-497 4370 4370 0 01-359-950 63 63 0 00-55-65 82 82 0 00-45 0l-12 7-17 10 10-14c-6037 3536-8085 10076-8274 13350a12025 12025 0 00-6614 2548 7136 7136 0 00-622-470 11134 11134 0 01-68-5873c-2468 1124-4390 2900-5785 4470h-10c-953-1206-886-5187-832-6018-10-52-710 363-802 425a17507 17507 0 00-2349 2012 21048 21048 0 00-2244 2692l-1 3v-3a20284 20284 0 00-3225 7280l-32 160a39700 39700 0 00-237 1500l-5 52a22907 22907 0 00-390 3316l-1 120c0 13270 10760 24030 24032 24030 11887 0 21756-8630 23690-19963l110-927c477-4120-53-8453-1560-12075zM20170 35545c113 53 220 112 334 164l16 10a12620 12620 0 01-350-174zm5506-14493zm19813-3060l-3-23 4 26z"/><use fill="url(#b)" xlink:href="#c"/><use fill="url(#d)" xlink:href="#c"/><path fill="url(#e)" d="M36192 19560l150 110a13070 13070 0 00-2231-2911C26640 9290 32150 563 33080 120l10-13c-6037 3535-8085 10076-8273 13348 280-20 560-43 844-43 4505 0 8430 2477 10530 6150z"/><use fill="url(#f)" xlink:href="#g"/><use fill="url(#h)" xlink:href="#g"/><path fill="url(#i)" d="M17083 15204a24404 24404 0 01498 330 11134 11134 0 01-67-5874c-2470 1125-4390 2900-5785 4470 115-3 3600-66 5354 1074z"/><path fill="url(#j)" d="M1822 26240c1855 10960 11788 19325 23063 19644 10437 296 17104-5762 19858-11670 1780-3820 3016-10070 748-16218v-2l-4-24c-2-17-4-28-4-22l5 40c853 5566-1980 10958-6405 14604l-13 30c-8625 7023-16878 4237-18550 3097a14410 14410 0 01-350-174c-5028-2403-7105-6984-6660-10913-4245 0-5693-3580-5693-3580s3812-2718 8836-355c4653 2190 9023 355 9023 354-10-195-4192-1860-5822-3465-872-860-1285-1272-1652-1583a7136 7136 0 00-622-470 28293 28293 0 00-498-330c-1753-1140-5240-1076-5355-1073h-10c-953-1207-886-5188-832-6020-10-50-710 363-802 426a17507 17507 0 00-2349 2012 21048 21048 0 00-2244 2692l-1 3v-3a20284 20284 0 00-3225 7280c-10 52-865 3784-444 5720z"/><path fill="url(#k)" d="M34110 16760a13070 13070 0 012231 2910l360 296c5450 5020 2594 12120 2380 12626 4426-3646 7258-9038 6405-14604-2716-6774-7323-9506-11086-15453l-566-920a7372 7372 0 01-265-497 4370 4370 0 01-359-950 63 63 0 00-55-65 82 82 0 00-45 0l-12 7-17 10c-930 443-6440 9170 1030 16640z"/><path fill="url(#l)" d="M36702 19965a4743 4743 0 00-360-295l-150-110c-1283-900-3580-1792-5794-1407 8644 4322 6323 19203-5655 18640a10674 10674 0 01-3127-603 13451 13451 0 01-706-289 9064 9064 0 01-405-193l16 10c1670 1140 9924 3925 18550-3097l13-30c213-506 3068-7606-2380-12626z"/><path fill="url(#m)" d="M14844 27844s1110-4133 7943-4133c740 0 2850-2062 2890-2660s-4370 1836-9023-354c-5024-2363-8836 354-8836 354s1448 3580 5693 3580c-445 3930 1632 8510 6660 10913 113 53 218 112 334 164-2935-1517-5358-4384-5660-7865z"/><path fill="url(#n)" d="M47870 16735c-1044-2512-3160-5224-4820-6082 1352 2650 2134 5310 2433 7294l5 40c-2718-6773-7325-9505-11088-15452l-566-920a7372 7372 0 01-265-497 4370 4370 0 01-359-950 63 63 0 00-55-65 82 82 0 00-45 0l-12 7-17 10 10-14c-6037 3536-8085 10076-8274 13350 280-20 560-43 845-43 4505 0 8430 2477 10530 6148-1284-900-3580-1792-5795-1407 8644 4322 6323 19203-5655 18640a10674 10674 0 01-3127-603 13451 13451 0 01-706-289 9064 9064 0 01-405-193l17 10a14410 14410 0 01-350-174c112 53 218 112 333 164-2935-1517-5358-4384-5660-7865 0 0 1108-4133 7942-4133 740 0 2850-2062 2890-2660-10-195-4190-1860-5822-3465-870-860-1285-1272-1650-1583a7136 7136 0 00-623-470 11134 11134 0 01-67-5873c-2470 1124-4390 2900-5785 4470h-10c-953-1207-886-5187-832-6020-10-50-710 363-802 426a17507 17507 0 00-2349 2012 21048 21048 0 00-2243 2692l-1 3v-3a20284 20284 0 00-3225 7280l-32 160a39787 39787 0 00-277 1515c-2 18 2-17 0 0a27956 27956 0 00-355 3353l-3 122c0 13270 10760 24030 24032 24030 11887 0 21756-8630 23690-19963l110-927c477-4120-53-8453-1560-12075zm-2384 1234l4 26v-2l-4-24z"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 -2 313 305"><defs><radialGradient xlink:href="#a" id="e" cx="173.96" cy="149.58" r="161.64" gradientTransform="matrix(0 1 -.8 0 293.62 -24.38)"><stop offset=".55" stop-color="#99fbff"/><stop offset=".61" stop-color="#92f6fe"/><stop offset=".7" stop-color="#80e7fa"/><stop offset=".8" stop-color="#62cff3"/><stop offset=".91" stop-color="#38adea"/><stop offset="1" stop-color="#0f8ce1"/></radialGradient><radialGradient xlink:href="#a" id="o" cx="62.968" cy="220.74" r="48.326" gradientTransform="matrix(3.4175 -2.0751 1.0888 1.7931 -389.83 -12.808)"><stop offset="0" stop-color="#fcf69f" stop-opacity="0"/><stop offset=".5" stop-color="#fcf69f" stop-opacity="0"/><stop offset="1" stop-color="#fcf69f" stop-opacity=".77"/></radialGradient><radialGradient xlink:href="#a" id="k" cx="100.32" cy="148.8" r="138.81" gradientTransform="matrix(.9302 -.83744 .32785 .36414 17.035 155.03)"><stop offset="0" stop-color="#f8b633" stop-opacity="0"/><stop offset=".83" stop-color="#fabc3f" stop-opacity="0"/><stop offset=".92" stop-color="#fdf8c3" stop-opacity=".98"/><stop offset=".96" stop-color="#fdf8c3" stop-opacity=".98"/><stop offset="1" stop-color="#fdf6b0" stop-opacity=".98"/></radialGradient><radialGradient xlink:href="#a" id="p" cx="138.27" cy="245.02" r="84.078" gradientTransform="matrix(1.3738 -1.4223 .47637 .46011 -146.79 229.99)"><stop offset="0" stop-color="#3d3a04" stop-opacity="0"/><stop offset=".924" stop-color="#454205" stop-opacity="0"/><stop offset="1" stop-color="#4e4a06" stop-opacity=".27"/></radialGradient><radialGradient xlink:href="#a" id="n" cx="92.079" cy="70.362" r="64.365" gradientTransform="matrix(1.82 .03223 -.03882 2.1923 -73.94 -85.114)"><stop offset="0" stop-color="#a0f5fc"/><stop offset="1" stop-color="#a0f3fc" stop-opacity="0"/></radialGradient><radialGradient xlink:href="#a" id="b" cx="136.1" cy="93.932" r="157.94" gradientTransform="matrix(.67825 -.45732 .55906 .82913 -15.248 63.774)"><stop offset="0" stop-color="#fcfcfc"/><stop offset=".681" stop-color="#fff"/><stop offset="1"/></radialGradient><radialGradient xlink:href="#a" id="h" cx="160.08" cy="156.25" r="138.81" gradientTransform="matrix(1.0704 -1.0538 .44083 .44775 -75.068 245.37)"><stop offset="0" stop-opacity="0"/><stop offset=".82" stop-color="#fdbb36" stop-opacity=".94"/><stop offset=".89" stop-color="#fbd44d" stop-opacity=".92"/><stop offset="1" stop-color="#fbcf39" stop-opacity=".88"/></radialGradient><radialGradient xlink:href="#a" id="i" cx="180.99" cy="167.23" r="138.81" gradientTransform="matrix(1.1348 -1.1322 .52964 .53081 -114.38 274.24)"><stop offset="0" stop-color="#f7ab32" stop-opacity="0"/><stop offset=".76" stop-color="#f7b535" stop-opacity=".92"/><stop offset=".84" stop-color="#f7b33f" stop-opacity=".96"/><stop offset=".93" stop-color="#f7e232"/><stop offset="1" stop-color="#f5ec80"/></radialGradient><radialGradient xlink:href="#a" id="m" cx="-2.5" cy="136.62" r="138.81" gradientTransform="matrix(.85784 -.85837 .28831 .28814 123.22 99.466)"><stop offset="0"/><stop offset=".94" stop-color="#fee259" stop-opacity=".5"/><stop offset="1" stop-color="#fff045" stop-opacity="0"/></radialGradient><radialGradient xlink:href="#a" id="c" cx="222.81" cy="221.24" r="156.35" gradientTransform="matrix(1.1049 -.66263 .37599 .62704 -95.652 198.56)"><stop offset="0"/><stop offset=".672"/><stop offset="1" stop-color="#fff"/></radialGradient><radialGradient xlink:href="#a" id="d" cx="101.97" cy="109.66" r="158" gradientTransform="matrix(.84189 -.58292 .28064 .4054 -11.053 122.85)"><stop offset="0" stop-color="#fff"/><stop offset=".69" stop-color="#fff"/><stop offset="1"/></radialGradient><mask id="l" maskUnits="userSpaceOnUse"><path fill="url(#b)" fill-rule="evenodd" stroke="#000" d="M.5-12.389h314.89v314.89H.5z"/></mask><mask id="f" maskUnits="userSpaceOnUse"><path fill="url(#c)" fill-rule="evenodd" d="M0-.374h312.71v302.26H0z"/></mask><mask id="j" maskUnits="userSpaceOnUse"><path fill="url(#d)" fill-rule="evenodd" stroke="#000" d="M.5-12.5h315v315H.5z"/></mask><linearGradient id="a" gradientUnits="userSpaceOnUse"/><path id="g" d="M108.75 260.53c-27.7 14.08-50 17.08-61.5 5.6-25-25 7.8-94.8 74.3-161.32S257.86 5.5 282.9 30.52c10.13 10.13 9 28.72-1.14 52q2.1 3.34 4 6.82c13.47-31.15 14.25-57.5-1.46-73.2-28.75-29.12-126.4 4.2-186.67 67.3S-.16 242.8 27.74 271.4c18.14 18.14 51.2 13.87 89.56-6.43q-4.38-2.05-8.55-4.43z"/></defs><path fill="url(#e)" stroke="#1b62a2" stroke-opacity=".878" stroke-width="4.913" d="M226.06 194.2c-10.23 17.2-29.93 28.53-51.3 28.53a56.08 56.08 0 0 1-56.28-56v-2.2H298a125.85 125.85 0 0 0 .77-13.88A124.27 124.27 0 0 0 174.44 25.82c-69.08 0-125.9 55.76-125.9 124.84 0 22.48 6.47 43.56 16.7 61.75 9.15 16.27 21.16 30.23 36.5 40.8 20.22 13.92 45.5 21.58 72 21.58 27.95 0 54.67-8.54 75.7-23.84A122 122 0 0 0 292 194.19zM176.56 79c30.52 0 53.3 24.3 54.13 54.6H118.2c.97-30.3 27.8-54.6 58.37-54.6z" paint-order="stroke markers fill"/><g mask="url(#f)"><use xlink:href="#g" fill="url(#h)"/><path fill="url(#i)" d="M108.75 260.53c-27.7 14.08-50 17.08-61.5 5.6-25-25 7.8-94.8 74.3-161.32S257.86 5.5 282.9 30.52c10.13 10.13 9 28.72-1.14 52q2.1 3.34 4 6.82c13.47-31.15 14.25-57.5-1.46-73.2-28.75-29.12-126.4 4.2-186.67 67.3S-.16 242.8 27.74 271.4c18.14 18.14 51.2 13.87 89.56-6.43q-4.38-2.05-8.55-4.43z" mask="url(#j)" opacity=".93"/><path fill="url(#k)" d="M108.75 260.53c-27.7 14.08-50 17.08-61.5 5.6-25-25 7.8-94.8 74.3-161.32S257.86 5.5 282.9 30.52c10.13 10.13 9 28.72-1.14 52q2.1 3.34 4 6.82c13.47-31.15 14.25-57.5-1.46-73.2-28.75-29.12-126.4 4.2-186.67 67.3S-.16 242.8 27.74 271.4c18.14 18.14 51.2 13.87 89.56-6.43q-4.38-2.05-8.55-4.43z" mask="url(#l)" opacity=".835"/><use xlink:href="#g" fill="url(#m)"/></g><path fill="url(#n)" d="M174.44 27.82c-68.937 0-125.66 55.53-125.89 124.41 13.174-23.455 29.981-46.791 49.086-66.793 23.061-24.144 51.592-43.919 79.645-57.514l-2.836-.105z" transform="translate(0 -2)"/><path fill="url(#o)" d="M46.15 154.56c12.851-24.577 33.552-52.853 50.53-70.16-19.184 16.918-38.242 38.928-50 53.93C4.16 193.01-8.37 240.79 5.33 264.03c11.08 18.8 32.27 23.17 53 18.83-12.4-.08-22.86-3.75-30.6-11.48-18.48-18.97-8.2-67.73 18.4-116.82z" opacity=".877"/><path fill="url(#p)" d="M217.33 35.551c-28.801 13.432-62.699 38.165-95.781 71.258-29.291 29.3-52.032 59.233-66.188 85.697a131.825 131.825 0 0 0 4.354 10.953c6.442-25.294 32.58-54.181 64.28-92.234 1.748-2.1 3.675-5.125 5.817-7.215a59.603 59.603 0 0 1 14.154-13.086c29.88-18.073 54.304-40.563 79.551-52.885a124.255 124.255 0 0 0-6.187-2.488z" transform="translate(0 -2)"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1090 1090"><linearGradient id="a" x1="461" x2="461" y1="59" y2="1033" gradientUnits="userSpaceOnUse"><stop offset=".6" stop-color="#ff1b2d"/><stop offset="1" stop-color="#a70014"/></linearGradient><linearGradient id="b" x1="714" x2="714" y1="116" y2="978" gradientUnits="userSpaceOnUse"><stop offset="0" stop-color="#9c0000"/><stop offset=".7" stop-color="#ff4b4b"/></linearGradient><path fill="url(#a)" d="M545 42.5a502.5 502.5 0 10334.9 877.1 362.4 362.4 0 01-201.4 61.5c-119.7 0-226.8-59.4-299-153-55.6-65.6-91.5-162.5-94-271.3V533c2.5-108.8 38.4-205.8 94-271.3 72-93.6 179.3-153 299-153 73.6 0 142.5 22.5 201.4 61.6a500.8 500.8 0 00-333-127.9h-2z"/><path fill="url(#b)" d="M379.6 261.8c46-54.4 105.7-87.3 170.7-87.3 146.3 0 265 166 265 370.4 0 204.6-118.6 370.4-265 370.4-65 0-124.6-32.8-170.7-87.2 72 93.6 179.2 153 299 153A363 363 0 00880 919.6 501 501 0 001047.5 545a501.1 501.1 0 00-167.6-374.6 362.4 362.4 0 00-201.4-61.5c-119.7 0-226.8 59.4-299 153"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 5120 5120"><linearGradient id="a" x2="0" y2="100%"><stop offset="0" stop-color="#19d7ff"/><stop offset="1" stop-color="#1e64f0"/></linearGradient><g fill="#fff"><rect width="5130" height="5120" rx="1050"/><circle cx="2560" cy="2560" r="2240" fill="url(#a)"/><path fill="red" d="M4090 1020 2370 2370l4e2 4e2z"/><path d="M1020 4090l1350-1720 4e2 4e2z"/></g><path stroke="#fff" stroke-width="30" d="M2560 540v330m0 3370v330m350-4e3-57 325m-586 3318-57 327M3250 662l-113 310M1984 4138l-113 310m339-3878 57 325m586 3318 57 327M1870 662l113 310m1152 3166 113 310M1552 810l166 286m1685 2918 165 286M1265 1010l212 253m2166 2582 212 253M1015 1258l253 212m2582 2168 253 212M813 1548l286 165m2920 1685 286 165M665 1866l310 113m3166 1150 310 113M574 2202l326 58m3320 588 325 57M545 2555h330m3370 0h330M575 2905l325-57m3320-586 325-57M668 3245l310-113m3165-1152 310-113M815 3563l286-165m2920-1685 286-165M1016 3850l253-212m2580-2166 253-212M1262 41e2l212-253m2166-2582 212-253M1552 43e2l166-286m1685-2918 165-286M2384 548l16 180m320 3656 16 180M2038 610l47 174m950 3544 47 174M1708 730l76 163m1550 3326 77 163M1404 904l103 148m2106 3006 103 148M1135 1130l127 127m2596 2596 127 127M910 14e2l148 103m3006 2107 146 1e2M734 1703l163 76m3326 1550 163 77M614 2033l174 47m3544 950 174 47M553 2380l180 16m3656 320 180 16m-4014 0 180-16m3656-320 180-16M614 3077l174-47m3544-950 174-47M734 3407l163-76m3326-1550 163-76M910 3710l148-103m3006-2107 146-1e2M1404 4206l103-148m2105-3006 104-148M1708 4380l77-163M3335 890l77-163M2038 45e2l47-174m950-3544 47-174m-698 3952 16-180m320-3656 16-180"/></svg>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 792 792" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><g><path d="M417.786,322.402c-0.653,-1.276 6.584,-1.928 7.889,-1.928c1.423,0.148 -3.767,8.63 -7.889,1.928Zm-41.224,-58.9l2.076,-0.593l-0.979,2.58c1.661,2.995 0.415,4.775 0.149,6.347l-2.848,1.809c-1.008,1.245 4.953,1.423 5.013,1.631c0.237,0.623 -7.207,1.72 -5.813,3.292c1.838,2.61 15.777,-3.737 13.583,-3.381c4.3,-2.165 0.593,-2.402 -1.839,-3.678c-0.83,-4.122 -1.542,-10.498 -4.122,-13.019l1.72,-1.928c-4.004,-5.753 -6.94,6.94 -6.94,6.94Zm256.537,132.331c-0,131.027 -106.233,237.26 -237.26,237.26c-131.026,-0 -237.259,-106.233 -237.259,-237.26c-0,-131.026 106.233,-237.259 237.259,-237.259c131.027,-0 237.26,106.233 237.26,237.259Zm-166.438,-103c-0.089,-2.817 -3.648,-5.457 -7.296,-0.623c-2.58,3.322 -2.135,8.423 -3.558,10.677c-1.987,3.381 10.914,6.525 10.914,3.351c0.504,-5.308 14.027,-1.216 16.667,-0.474c4.745,1.334 12.337,-4.33 4.063,-7.296c-6.851,-2.462 -10.439,-5.131 -11.032,-9.995c-0,0 3.588,-3.38 2.016,-3.173c-4.181,0.475 -11.774,15.036 -11.774,7.533Zm120.765,103c0,-19.84 -3.381,-39.889 -6.851,-50.477c-1.097,-3.321 -3.647,-5.99 -6.88,-7.236c-4.923,-1.898 -25.654,11.448 -28.798,4.864l-10.943,0.148c-2.313,-1.275 -8.69,-9.876 -11.655,-8.808c-5.873,2.135 9.104,18.506 13.197,20.641c3.885,-2.936 16.312,-8.926 19.011,-0.741c5.16,15.451 -14.147,32.297 -23.964,41.224c-14.739,13.316 -12.011,-8.631 -22.035,-16.341c-5.279,-4.034 -5.22,-12.664 -10.529,-15.63c-2.372,-1.334 -13.256,-13.909 -13.197,-15.6l-0.267,3.174c-1.809,1.364 -5.635,-5.131 -6.05,-6.169c-0,5.664 9.223,14.68 12.219,19.188c5.219,7.77 8.007,19.1 14.324,25.417c3.441,3.44 16.49,17.527 19.871,17.231l17.468,-8.275c12.337,2.936 -29.035,61.48 -32.949,68.716c-3.233,6.08 2.609,21.087 2.135,28.294c-0.593,8.304 -7.118,11.003 -13.257,15.51c-6.673,4.835 -5.101,14.266 -10.706,17.736c-9.906,6.109 -16.994,25.95 -31.081,25.861c-4.152,-0 -21.858,6.91 -24.23,0.118c-1.809,-4.893 -4.182,-8.63 -6.733,-13.494c-2.491,-4.745 -0.296,-9.668 -3.292,-13.85c-2.135,-2.936 -9.134,-9.549 -9.757,-12.99c-0.03,-2.965 2.224,-12.011 5.368,-13.583c4.389,-2.254 0.86,-8.778 0.326,-12.574c-0.889,-6.762 -5.101,-12.368 -10.172,-16.312c-7.474,-5.724 -3.589,-10.291 -1.839,-18.477c0,-3.914 -2.373,-9.075 -7.622,-7.503c-10.854,3.144 -7.563,-8.452 -15.451,-7.918c-5.665,0.415 -10.321,4.003 -15.571,5.575c-6.672,1.987 -13.434,-1.572 -19.989,-2.402c-26.988,-3.411 -35.796,-34.254 -28.797,-56.527l-0.89,-13.198c3.055,-6.762 9.224,-14.324 14.621,-19.455c3.025,-2.877 6.911,-2.135 10.499,-4.36c5.516,-3.47 5.576,-10.617 10.944,-14.977c7.681,-6.228 18.15,-6.109 28.145,-7.444c5.338,-0.711 25.624,-5.101 28.856,-1.156c0,0.711 3.648,11.596 -0.385,10.973c8.274,0.445 20.137,14.354 27.996,11.121c4.063,-1.69 2.61,-14.116 10.885,-8.096c5.041,3.618 27.551,5.22 32.237,1.335c2.907,-2.373 4.478,-17.854 0.979,-19.574c2.195,2.194 -11.774,2.372 -13.079,1.868c-2.284,-0.83 -4.419,2.195 -8.126,0.504c2.224,1.038 -12.367,-6.791 -4.182,-12.812l-10.35,2.047l-2.462,5.279c-5.753,2.936 -10.143,-10.084 -12.337,-11.537c-2.225,-1.483 -19.456,-13.554 -14.77,-5.665l15.185,15.066c-0.742,0.505 -4.004,-5.486 -4.004,-1.127c0.979,-2.58 0.415,11.122 -1.987,6.644l0.089,-5.131c0,-1.631 -4.33,-3.203 -5.22,-4.33c-2.372,-2.995 -8.749,-9.55 -12.219,-11.122c-0.919,-0.444 -14.621,1.691 -15.777,2.136l-3.441,5.961l-8.037,4.093l-2.965,6.761c-1.365,1.157 -14.711,5.576 -14.77,5.754c0.593,-1.453 -9.312,-3.292 -8.689,-6.169c0.741,-3.143 4.122,-13.019 3.232,-16.667c-0.919,-3.796 20.582,5.457 21.976,-4.508c0.564,-4.3 0.86,-9.342 -6.05,-10.054c1.364,0.148 13.346,-4.716 15.363,-6.91c2.817,-3.203 9.253,-8.482 13.879,-8.482c5.457,-0 4.271,-7.919 6.792,-11.774c2.521,1.008 -1.364,7.207 1.69,9.727c-0.207,-1.957 8.542,1.098 9.372,0.623c1.987,-1.038 13.109,-0.445 11.389,-5.664c-1.869,-5.339 0.978,-3.737 3.47,-4.864c-0.416,0.178 6.524,-11.863 7.74,-7.919c-0.83,-4.063 -8.067,1.424 -10.588,1.216c-5.812,-0.474 -3.38,-9.965 -1.186,-12.752c1.691,-2.225 -4.626,-4.924 -4.745,-0.683c-0.059,6.317 -5.931,12.012 -4.567,20.405c2.016,12.634 -14.147,-3.055 -15.511,-2.195c-5.398,3.233 -9.787,-4.093 -6.999,-8.541l12.515,-9.135c1.987,-3.41 4.33,-7.384 7.385,-9.965c10.291,-8.63 13.079,-1.72 23.311,-0.771c10.024,0.92 3.351,2.373 2.016,6.199c-1.334,3.677 5.517,4.952 7.86,1.898c1.364,-1.78 4.359,-6.199 5.694,-9.461c1.72,-4.241 17.26,-3.766 6.406,-10.291c-7.148,-4.271 -38.436,-12.901 -59.374,-12.901c-4.538,-0 -7.682,5.042 -11.152,7.918c-6.821,5.635 -24.289,16.757 -34.046,13.406c-9.936,-3.441 -31.259,12.663 -34.64,12.752c-1.275,0.06 -0,-12.189 6.821,-13.079c-2.966,0.445 23.874,-13.553 23.162,-16.489c-0.86,-3.44 -53.679,15.778 -51.307,19.633c1.127,1.78 5.724,1.78 -0.296,5.635c-3.47,2.076 -7.178,15.363 -10.381,15.363c-9.697,4.241 -10.38,-8.334 -21.116,7.859l-17.171,6.91c-25.476,27.048 -43.092,61.302 -49.469,99.412c-0.237,1.512 6.377,4.33 7.237,5.368c2.135,2.58 2.135,13.642 3.173,17.29c2.669,9.164 9.223,14.265 14.206,22.599c2.995,4.953 7.859,17.527 6.317,22.747c2.076,-3.411 20.523,15.63 23.904,19.604c7.948,9.342 14.087,20.671 1.156,29.894c-4.152,2.996 6.317,21.65 0.92,26.247l-6.94,1.78c-6.792,4.181 -3.737,14.472 0.415,18.832c34.907,36.449 84.049,59.196 138.5,59.196c105.759,0 191.587,-85.769 191.587,-191.587Zm-220.71,-121.921c2.402,-1.068 5.605,-1.009 5.961,-4.212l1.512,-1.898c0.682,-1.008 -1.275,-2.639 -2.046,-2.817l-2.877,1.987l-1.394,0.356l-1.305,1.661l0.149,0.919l-1.691,2.017c-1.631,1.66 0.06,2.698 1.691,1.987Zm-257.842,48.312c-41.016,-55.134 -89.981,-136.84 -59.344,-189.185c9.015,-15.392 28.441,-33.78 69.843,-33.78c40.779,0 90.396,17.943 132.48,37.546c-17.914,9.965 -34.551,21.799 -49.914,35.144c-34.492,-13.761 -62.785,-20.641 -82.566,-20.641c-7.563,-0 -20.82,1.038 -24.912,8.037c-5.784,9.876 -3.233,42.529 36.626,103.564c-9.431,18.714 -16.875,38.584 -22.213,59.315Zm480.866,-150.275c22.48,-8.956 95.259,-33.483 107.478,-12.604c11.982,20.434 -15.451,70.733 -36.657,103.534c9.461,18.744 16.905,38.614 22.244,59.345c40.986,-55.104 89.95,-136.84 59.344,-189.155c-9.016,-15.422 -28.441,-33.81 -69.843,-33.81c-40.75,0 -90.366,17.943 -132.45,37.546c17.883,9.965 34.551,21.799 49.884,35.144Zm93.065,297.494c-5.339,20.731 -12.783,40.601 -22.244,59.345c39.801,60.946 42.47,93.628 36.657,103.534c-12.219,20.908 -84.88,-3.737 -107.39,-12.694c-15.421,13.376 -32.089,25.268 -50.061,35.263c41.105,19.129 91.345,37.517 132.539,37.517c41.402,-0 60.827,-18.388 69.813,-33.78c30.903,-52.731 -19.129,-135.09 -59.314,-189.185Zm-480.955,150.215c-22.51,8.956 -95.171,33.602 -107.389,12.693c-5.784,-9.905 -3.114,-42.588 36.656,-103.534c-9.431,-18.743 -16.875,-38.584 -22.213,-59.315c-40.216,54.066 -90.218,136.454 -59.374,189.126c9.015,15.392 28.441,33.78 69.843,33.78c41.224,-0 91.434,-18.388 132.539,-37.487c-17.973,-10.025 -34.64,-21.888 -50.062,-35.263Z" style="fill-rule:nonzero;"/></g></svg>
@@ -0,0 +1,2 @@
1
+ Downloaded from https://github.com/edent/SuperTinyIcons
2
+ Update with `rake download_logos`
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg"
2
+ aria-label="Android" role="img"
3
+ viewBox="0 0 512 512" ><rect
4
+ width="512" height="512"
5
+ rx="15%"
6
+ fill="#fff"/><path d="m338.2 305.7a14.9 14.9 0 1 1 14.9-14.9 14.9 14.9 0 0 1-14.9 14.9m-164.3 0a14.9 14.9 0 1 1 14.9-14.9 14.9 14.9 0 0 1-14.9 14.9m169.6-89.5 29.7-51.5a6.2 6.2 0 1 0-10.7-6.2l-30.1 52.1c-23-10.5-48.8-16.3-76.4-16.3s-53.4 5.9-76.4 16.3l-30.1-52.1a6.2 6.2 0 1 0-10.7 6.2l29.7 51.5c-51 27.7-85.9 79.4-91 140.4h357c-5.1-61-40-112.7-91-140.4" fill="#3ddc84"/></svg>
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg"
2
+ aria-label="Apple" role="img"
3
+ viewBox="0 0 512 512"><rect
4
+ width="512" height="512"
5
+ rx="15%"
6
+ fill="#555"/><path fill="#f2f2f2" d="M410 334s-10 29-30 59c-5 9-29 43-58 43-21 0-35-15-62-15-33 0-46 15-67 15-11 1-22-5-34-16-77-73-81-181-52-225 18-29 48-47 81-48 26 0 54 17 65 17 8 0 50-20 74-18 33 3 56 15 73 38-49 24-66 117 10 150zM329 56c8 32-27 93-79 90-3-43 34-87 79-90z"/></svg>
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg"
2
+ aria-label="Linux" role="img"
3
+ viewBox="0 0 512 512" fill="#333"><rect
4
+ width="512" height="512"
5
+ rx="15%"
6
+ fill="#fff"/><g transform="matrix(2 0 0 2 256 256)"><path d="M-32-25c-3 7-24 29-22 51 8 92 36 30 78 53 0 0 75-42 15-110-17-24-2-43-13-59s-30-17-44-2 6 37-14 67"/><path d="M42 21s9-18-8-31c16 17 6 32 6 32h-3C36-13 27 6 14-56 29-73 0-88 0-60h-9c1-24-20-12-8 5-1 37-23 52-23 78-7-18 6-32 6-32s-18 15-7 37 31 17 17 27c22 15 56 5 55-27 1-8 22-5 24-3s-3-4-13-4m-56-78c-7-2-5-11-2-11s8 7 2 11m19 1c-5-7-1-14 4-13s5 13-4 13" fill="#eee"/><g fill="#fc2" stroke="#333" stroke-width="1"><path d="M-41 31l21 30c11 7 5 35-25 21-17-5-31-4-33-13s4-10 3-14c-4-22 14-11 19-22s5-16 15-2M71 45c-4-6 0-17-14-16-6 12-23 24-24 0-10 0-3 24-7 35-9 27 17 29 28 16l26-18c2-3 5-6-9-17m-92-92c-3-6 11-14 16-14s12 4 19 6 4 9 2 10S3-35-5-35s-10-8-16-12"/><path d="M-21-48c8 6 17 11 35-3"/></g><path d="M-10-54c-2 0 1-2 2-1m7 1c1-1-1-2-3-1"/></g></svg>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" encoding="UTF-8" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg width="100%" height="100%" viewBox="0 0 2134 2134" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><path d="M2133.33,320c0,-176.613 -143.387,-320 -320,-320l-1493.33,0c-176.613,0 -320,143.387 -320,320l0,1493.33c0,176.613 143.387,320 320,320l1493.33,0c176.613,0 320,-143.387 320,-320l0,-1493.33Z" style="fill:#fff;"/><g><path d="M1726.15,486.324c-0,-43.723 -35.414,-79.137 -79.138,-79.137l-1160.68,-0c-43.723,-0 -79.137,35.414 -79.137,79.137l-0,778.187l1318.96,-0l-0,-778.187Zm-131.896,646.291l-1055.17,-0l-0,-593.532l1055.17,-0l-0,593.532Zm131.896,197.844l-1318.96,-0c-12.992,24.532 -131.896,302.173 -131.896,329.608c-0,34.425 27.566,66.08 66.08,66.08l1450.59,-0c38.513,-0 66.08,-31.655 66.08,-66.08c-0,-27.435 -118.905,-305.076 -131.896,-329.608Zm-806.478,329.74l30.797,-65.948l232.335,-0l30.798,65.948l-293.93,-0Z" style="fill-rule:nonzero;"/></g></svg>
@@ -0,0 +1,6 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg"
2
+ aria-label="Windows" role="img"
3
+ viewBox="0 0 512 512"><rect
4
+ width="512" height="512"
5
+ rx="15%"
6
+ fill="#00adef"/><path fill="#fff" d="M98 145V250H225V127m15-2V250H408V101M98 368V263H225V386m15 1V263H408V411"/></svg>