trek 2.3.1 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. checksums.yaml +4 -4
  2. data/.node-version +1 -1
  3. data/.yarn/releases/yarn-4.18.1.cjs +1000 -0
  4. data/.yarnrc.yml +3 -5
  5. data/AGENTS.md +7 -4
  6. data/CHANGELOG.md +55 -0
  7. data/Gemfile.lock +35 -1
  8. data/Makefile +12 -0
  9. data/app/assets/icons/trek/passkey.svg +5 -0
  10. data/app/assets/stylesheets/trek/base.css +6 -0
  11. data/app/components/trek/entries/item_component/item_component.css +6 -0
  12. data/app/components/trek/form/image_field_component/image_field_component.html.slim +9 -0
  13. data/app/components/trek/form/password_strength_component/password_strength_component.css +56 -0
  14. data/app/components/trek/form/password_strength_component/password_strength_component.html.slim +8 -0
  15. data/app/components/trek/form/password_strength_component/password_strength_component.js +74 -0
  16. data/app/components/trek/form/password_strength_component.rb +29 -0
  17. data/app/components/trek/gate_component/gate_component.css +7 -0
  18. data/app/components/trek/icon/sprite_component.rb +3 -2
  19. data/app/components/trek/section_component/section_component.css +22 -0
  20. data/app/components/trek/section_component/section_component.html.slim +5 -0
  21. data/app/components/trek/section_component/section_component.js +1 -0
  22. data/app/components/trek/section_component.rb +14 -0
  23. data/app/controllers/concerns/trek/auditable.rb +103 -0
  24. data/app/controllers/concerns/trek/filters.rb +2 -0
  25. data/app/controllers/concerns/trek/passkeys.rb +37 -0
  26. data/app/controllers/concerns/trek/scopes.rb +9 -2
  27. data/app/controllers/trek/csp_reports_controller.rb +62 -0
  28. data/app/controllers/trek/resource_controller.rb +7 -1
  29. data/app/helpers/trek/audit_logs_helper.rb +21 -0
  30. data/app/helpers/trek/user_agents_helper.rb +36 -0
  31. data/app/javascript/controllers/index.js +3 -0
  32. data/app/javascript/controllers/passkey_controller.js +118 -0
  33. data/app/javascript/trek.js +4 -0
  34. data/app/jobs/trek/audit_log/purge_job.rb +18 -0
  35. data/app/jobs/trek/purge_expired_sessions_job.rb +14 -0
  36. data/app/jobs/trek/resolve_session_hostname_job.rb +17 -0
  37. data/app/models/concerns/trek/audit_log.rb +30 -0
  38. data/app/models/concerns/trek/users/passkeys.rb +75 -0
  39. data/app/models/concerns/trek/users/password_history.rb +51 -0
  40. data/app/models/concerns/trek/users/password_policy.rb +85 -0
  41. data/app/models/concerns/trek/users/password_renewal.rb +51 -0
  42. data/app/models/concerns/trek/users/session_lifetime.rb +39 -0
  43. data/app/models/concerns/trek/users/sign_in_lockout.rb +67 -0
  44. data/app/models/concerns/trek/users/sign_ins.rb +31 -0
  45. data/app/models/concerns/trek/users/validations.rb +3 -2
  46. data/app/models/trek/feature.rb +6 -0
  47. data/app/models/trek/pwned_passwords.rb +40 -0
  48. data/app/models/trek/user_session.rb +10 -0
  49. data/app/policies/trek/audit_log_policy.rb +34 -0
  50. data/app/policies/trek/user_policy.rb +5 -0
  51. data/app/validators/not_disposable_validator.rb +18 -0
  52. data/config/brakeman.ignore +22 -0
  53. data/config/i18n-tasks.yml +7 -0
  54. data/config/locales/trek.en.yml +144 -39
  55. data/config/locales/trek.fr.yml +145 -40
  56. data/docs/.vitepress/config.mjs +8 -3
  57. data/docs/deploy.md +2 -0
  58. data/docs/proxies.md +53 -0
  59. data/docs/reference/components.md +1 -0
  60. data/docs/reference/controllers.md +1 -1
  61. data/docs/reference/environment-variables.md +2 -0
  62. data/docs/reference/generators/admin-user.md +1 -1
  63. data/docs/reference/generators/audit-log.md +113 -0
  64. data/docs/reference/generators/install.md +12 -3
  65. data/docs/reference/generators/security-headers.md +109 -0
  66. data/docs/reference/index.md +3 -0
  67. data/docs/reference/models/tag.md +4 -2
  68. data/docs/reference/models/user.md +123 -3
  69. data/docs/reference/validators.md +94 -0
  70. data/docs/security.md +90 -0
  71. data/lefthook.yml +8 -0
  72. data/lib/generators/trek/install/admin/user_password_resets_generator.rb +5 -0
  73. data/lib/generators/trek/install/admin/user_sessions_generator.rb +5 -0
  74. data/lib/generators/trek/install/admin/users_generator.rb +23 -0
  75. data/lib/generators/trek/install/audit_log_generator.rb +56 -0
  76. data/lib/generators/trek/install/authentication_generator.rb +77 -3
  77. data/lib/generators/trek/install/esbuild_generator.rb +1 -2
  78. data/lib/generators/trek/install/mailer_generator.rb +18 -0
  79. data/lib/generators/trek/install/model_specs_generator.rb +6 -4
  80. data/lib/generators/trek/install/postcss_generator.rb +1 -4
  81. data/lib/generators/trek/install/security_headers_generator.rb +49 -0
  82. data/lib/generators/trek/install/yarn_generator.rb +1 -1
  83. data/lib/generators/trek/install_generator.rb +62 -54
  84. data/lib/generators/trek/taxonomies_generator.rb +5 -0
  85. data/lib/generators/trek/templates/.gitlab-ci.yml +18 -2
  86. data/lib/generators/trek/templates/controllers/admin/active_sessions_controller.rb +21 -0
  87. data/lib/generators/trek/templates/controllers/admin/audit_logs_controller.rb +30 -0
  88. data/lib/generators/trek/templates/controllers/admin/passkey_sessions_controller.rb +41 -0
  89. data/lib/generators/trek/templates/controllers/admin/passkeys_controller.rb +37 -0
  90. data/lib/generators/trek/templates/controllers/admin/user_password_resets_controller.rb +4 -4
  91. data/lib/generators/trek/templates/controllers/admin/user_sessions_controller.rb +53 -4
  92. data/lib/generators/trek/templates/controllers/admin/users_controller.rb +20 -0
  93. data/lib/generators/trek/templates/controllers/concerns/trek/authentication.rb +58 -7
  94. data/lib/generators/trek/templates/helpers/application_helper.rb +0 -4
  95. data/lib/generators/trek/templates/initializers/audit_log.rb +11 -0
  96. data/lib/generators/trek/templates/initializers/content_security_policy.rb +49 -0
  97. data/lib/generators/trek/templates/initializers/permissions_policy.rb +19 -0
  98. data/lib/generators/trek/templates/jobs/admin/password_reset_job.rb +9 -0
  99. data/lib/generators/trek/templates/layouts/admin/auth.html.slim +0 -5
  100. data/lib/generators/trek/templates/layouts/admin.html.slim +0 -3
  101. data/lib/generators/trek/templates/mailers/admin/passwords_mailer.rb +1 -1
  102. data/lib/generators/trek/templates/mailers/admin_mailer.rb +28 -1
  103. data/lib/generators/trek/templates/migrations/audit_logs.rb.erb +27 -0
  104. data/lib/generators/trek/templates/migrations/tags.rb.erb +6 -1
  105. data/lib/generators/trek/templates/migrations/user_passkeys.rb.erb +24 -0
  106. data/lib/generators/trek/templates/migrations/user_password_renewal.rb.erb +8 -0
  107. data/lib/generators/trek/templates/migrations/user_previous_passwords.rb.erb +10 -0
  108. data/lib/generators/trek/templates/migrations/user_session_tracking.rb.erb +11 -0
  109. data/lib/generators/trek/templates/migrations/user_sign_in_lockout.rb.erb +7 -0
  110. data/lib/generators/trek/templates/migrations/user_sign_ins.rb.erb +13 -0
  111. data/lib/generators/trek/templates/models/audit_log.rb +3 -0
  112. data/lib/generators/trek/templates/models/tag_category.rb +2 -1
  113. data/lib/generators/trek/templates/models/user/passkey.rb +17 -0
  114. data/lib/generators/trek/templates/models/user/previous_password.rb +4 -0
  115. data/lib/generators/trek/templates/models/user/session.rb +8 -0
  116. data/lib/generators/trek/templates/policies/audit_log.rb +4 -0
  117. data/lib/generators/trek/templates/spec/mailers/admin/passwords_mailer_spec.rb +16 -0
  118. data/lib/generators/trek/templates/spec/mailers/admin_mailer_spec.rb +50 -0
  119. data/lib/generators/trek/templates/spec/models/audit_log_spec.rb +43 -0
  120. data/lib/generators/trek/templates/spec/models/tag_spec.rb +52 -0
  121. data/lib/generators/trek/templates/spec/models/user/session_spec.rb +68 -0
  122. data/lib/generators/trek/templates/spec/models/user_spec.rb +142 -4
  123. data/lib/generators/trek/templates/spec/requests/admin/active_sessions_spec.rb +119 -0
  124. data/lib/generators/trek/templates/spec/requests/admin/audit_logs_spec.rb +149 -0
  125. data/lib/generators/trek/templates/spec/requests/admin/passkey_sessions_spec.rb +103 -0
  126. data/lib/generators/trek/templates/spec/requests/admin/passkeys_spec.rb +104 -0
  127. data/lib/generators/trek/templates/spec/requests/admin/password_renewal_spec.rb +83 -0
  128. data/lib/generators/trek/templates/spec/requests/admin/sign_in_lockout_spec.rb +95 -0
  129. data/lib/generators/trek/templates/spec/requests/admin/sign_in_notices_spec.rb +93 -0
  130. data/lib/generators/trek/templates/spec/requests/admin/user_password_resets_spec.rb +55 -6
  131. data/lib/generators/trek/templates/spec/requests/admin/user_sessions_spec.rb +31 -2
  132. data/lib/generators/trek/templates/views/admin/audit_logs/index.html.slim +35 -0
  133. data/lib/generators/trek/templates/views/admin/audit_logs/show.html.slim +50 -0
  134. data/lib/generators/trek/templates/views/admin/user_password_resets/edit.html.slim +17 -7
  135. data/lib/generators/trek/templates/views/admin/user_password_resets/new.html.slim +3 -2
  136. data/lib/generators/trek/templates/views/admin/user_sessions/new.html.slim +28 -2
  137. data/lib/generators/trek/templates/views/admin/users/_passkeys.html.slim +67 -0
  138. data/lib/generators/trek/templates/views/admin/users/_properties.html.slim +21 -0
  139. data/lib/generators/trek/templates/views/admin/users/_sessions.html.slim +70 -0
  140. data/lib/generators/trek/templates/views/admin/users/index.html.slim +13 -0
  141. data/lib/generators/trek/templates/views/admin/users/show.html.slim +10 -21
  142. data/lib/generators/trek/templates/views/admin_mailer/new_sign_in.en.html.slim +22 -0
  143. data/lib/generators/trek/templates/views/admin_mailer/new_sign_in.en.text.erb +16 -0
  144. data/lib/generators/trek/templates/views/admin_mailer/new_sign_in.fr.html.slim +22 -0
  145. data/lib/generators/trek/templates/views/admin_mailer/new_sign_in.fr.text.erb +16 -0
  146. data/lib/generators/trek/templates/views/admin_mailer/sign_in_locked.en.html.slim +17 -0
  147. data/lib/generators/trek/templates/views/admin_mailer/sign_in_locked.en.text.erb +10 -0
  148. data/lib/generators/trek/templates/views/admin_mailer/sign_in_locked.fr.html.slim +18 -0
  149. data/lib/generators/trek/templates/views/admin_mailer/sign_in_locked.fr.text.erb +10 -0
  150. data/lib/trek/admin_ip_allowlist.rb +52 -0
  151. data/lib/trek/csp_reports.rb +99 -0
  152. data/lib/trek/data/common_passwords.txt +857 -0
  153. data/lib/trek/data/passkey_providers.json +404 -0
  154. data/lib/trek/disposable_email_domains.rb +53 -0
  155. data/lib/trek/engine.rb +12 -0
  156. data/lib/trek/passkey_providers.rb +19 -0
  157. data/lib/trek/reverse_dns.rb +27 -0
  158. data/lib/trek/version.rb +1 -1
  159. data/lib/trek.rb +13 -0
  160. data/package.json +3 -5
  161. data/renovate.json +11 -1
  162. data/trek.gemspec +3 -0
  163. data/yarn.lock +1757 -2019
  164. metadata +132 -4
  165. data/.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs +0 -541
  166. data/.yarn/releases/yarn-3.8.0.cjs +0 -875
  167. data/lib/generators/trek/templates/models/user_session.rb +0 -3
@@ -1,20 +1,20 @@
1
1
  ---
2
2
  en:
3
3
  activerecord:
4
- models:
5
- fragment:
6
- gender: "n"
7
- one: Fragment
8
- other: Fragments
9
- page:
10
- gender: "n"
11
- one: Page
12
- other: Pages
13
- user:
14
- gender: "n"
15
- one: User
16
- other: Users
17
4
  attributes:
5
+ audit_log:
6
+ action: Action
7
+ actor: Author
8
+ auditable: Target
9
+ created_at: Date
10
+ error_class: Error
11
+ http_method: Method
12
+ http_status: Status
13
+ ip_address: IP address
14
+ params: Parameters
15
+ path: Path
16
+ request_id: Request ID
17
+ user_agent: User agent
18
18
  fragment:
19
19
  content: Content
20
20
  key: Key
@@ -30,6 +30,41 @@ en:
30
30
  role: Role
31
31
  send_invite: Send invitation by email
32
32
  theme: Theme
33
+ user/passkey:
34
+ name: Name
35
+ errors:
36
+ models:
37
+ user:
38
+ attributes:
39
+ password:
40
+ common: is among the most common passwords, choose a less predictable one
41
+ digit_and_symbol: must contain a digit and a symbol, unless it is at least %{count} characters long
42
+ personal: must not contain your name or email
43
+ pwned: appears in known data breaches, choose another one
44
+ recently_used:
45
+ one: is your current password, choose another one
46
+ other: is one of your last %{count} passwords, choose another one
47
+ sequence: must not contain a sequence such as 12345, azerty or aaaaa
48
+ models:
49
+ audit_log:
50
+ gender: "n"
51
+ one: Audit log
52
+ other: Audit logs
53
+ fragment:
54
+ gender: "n"
55
+ one: Fragment
56
+ other: Fragments
57
+ page:
58
+ gender: "n"
59
+ one: Page
60
+ other: Pages
61
+ user:
62
+ gender: "n"
63
+ one: User
64
+ other: Users
65
+ user/passkey:
66
+ one: Passkey
67
+ other: Passkeys
33
68
  admin:
34
69
  actions:
35
70
  all: All
@@ -40,18 +75,30 @@ en:
40
75
  logout: Log out
41
76
  manage: Manage
42
77
  new_resource:
43
- n: New %{resource}
44
- reorder_resource:
45
- n: Reorder %{resources}
78
+ "n": New %{resource}
46
79
  remove: Remove
80
+ reorder_resource:
81
+ "n": Reorder %{resources}
47
82
  replace: Replace
48
83
  search_resource:
49
- n: Search for a %{resource}
50
- see_website: See website
84
+ "n": Search for a %{resource}
51
85
  see_on_website: See on website
86
+ see_original: See original
87
+ see_website: See website
52
88
  validate: Validate
53
89
  write: Write
90
+ active_sessions:
91
+ destroy:
92
+ success: Session signed out
93
+ destroy_others:
94
+ success: Signed out everywhere else
54
95
  attachments: Attachments
96
+ audit_logs:
97
+ index:
98
+ failures: Failures (%{count})
99
+ intro: Changes made in the admin, by whom and on what
100
+ show:
101
+ intro: Audit log entry
55
102
  authentication:
56
103
  not_authenticated: Please log in
57
104
  not_authorized: You are not authorized to perform this action
@@ -59,8 +106,8 @@ en:
59
106
  "false": "No"
60
107
  "true": "Yes"
61
108
  brand:
62
- title: Admin panel
63
109
  subtitle: Trek v%{version}
110
+ title: Admin panel
64
111
  content: Content
65
112
  content_editor:
66
113
  link:
@@ -149,6 +196,15 @@ en:
149
196
  success: Action added
150
197
  form:
151
198
  title: Action
199
+ passkey_sessions:
200
+ create:
201
+ failure: Signing in with this passkey didn't work, please try again
202
+ passkeys:
203
+ create:
204
+ failure: The passkey couldn't be added, please try again
205
+ success: Passkey added
206
+ destroy:
207
+ success: Passkey deleted
152
208
  passwords_mailer:
153
209
  reset:
154
210
  subject: Reset your password
@@ -166,21 +222,25 @@ en:
166
222
  seo: SEO
167
223
  settings: Settings
168
224
  user_password_resets:
169
- not_found: Invalid or expired password reset link.
170
225
  create:
171
- success: A password recovery email has been sent to you
226
+ success: A link to choose a new password has been sent to you (unless no account matches this email)
227
+ edit:
228
+ rules: At least %{min} characters with a digit and a symbol, or at least %{passphrase} characters.
172
229
  new:
173
230
  remembered_password: I found my password!
231
+ not_found: Invalid or expired password reset link.
174
232
  update:
175
233
  success: Your password has been changed
176
234
  user_sessions:
177
235
  create:
178
- failure: Connection failure, please check your credentials
179
- success: You are now logged in
236
+ failure: Wrong email or password. After several failures in a row, signing in is paused for a few minutes.
237
+ success: You're signed in, welcome!
180
238
  destroy:
181
239
  success: You are now logged out
182
240
  new:
183
241
  forgot_password: I forgot my password
242
+ passkey: Sign in with a passkey
243
+ passkey_failure: Signing in with a passkey didn't work, please try again.
184
244
  users:
185
245
  create:
186
246
  success: User added
@@ -191,24 +251,61 @@ en:
191
251
  intro: Updating the user
192
252
  index:
193
253
  intro: Management of admin panel users
254
+ require_password_renewal: Ask everyone for a new password
255
+ require_password_renewal_confirm: Every user, you included, will have to choose a new password at their next action. Use it when a compromise is suspected.
194
256
  new:
195
257
  intro: Adding a user to the admin panel
258
+ passkeys:
259
+ add: Add a passkey
260
+ added: Added on %{date}
261
+ already_added: This device or password manager already has a passkey for your account.
262
+ delete: Delete
263
+ delete_confirm: You won't be able to sign in with this passkey anymore. Also delete it from your device or password manager.
264
+ failure: The passkey couldn't be added, please try again.
265
+ intro: Sign in with your fingerprint, face or device PIN instead of your password. The passkey stays on your device or in your password manager.
266
+ last_used: Last used %{time} ago
267
+ never_used: Never used
268
+ title: Sign in without a password
269
+ require_password_renewal:
270
+ success: Every user will have to choose a new password
196
271
  search:
197
272
  alert: No user found with email
273
+ sessions:
274
+ change_password: Change your password
275
+ current: This browser
276
+ intro: Sign out any you don't recognize.
277
+ last_activity: Last activity %{time} ago
278
+ not_you_html: Not you? %{change_password}.
279
+ previous_sign_in: Previous sign-in
280
+ sign_out: Sign out
281
+ sign_out_others: Sign out everywhere else
282
+ sign_out_others_confirm: Every other browser signed in to your account will be signed out.
283
+ signed_in: Signed in on %{date}
284
+ title: Browsers currently signed in to this account
198
285
  show:
286
+ active_sessions: Active sessions
199
287
  intro: User profile
200
- profil: Profile
288
+ passkeys: Passkeys
289
+ profile: Profile
201
290
  subscription: Subscription
202
291
  update:
203
292
  success: User updated
204
- application_name: "Trek"
205
- views:
206
- pagination:
207
- first: First
208
- last: Last
209
- next: Next
210
- prev: Prev
211
- truncate: …
293
+ admin_mailer:
294
+ invite:
295
+ subject: Your invitation
296
+ new_sign_in:
297
+ subject: New sign-in to your account
298
+ sign_in_locked:
299
+ subject: Signing in to your account is paused
300
+ application_name: Trek
301
+ errors:
302
+ messages:
303
+ disposable: is a disposable email address, use a permanent one
304
+ notices:
305
+ authentication_needed: Please log in
306
+ password_renewal_needed: "Your password must be renewed: choose a new one"
307
+ rate_limited: Too many attempts, please try again later
308
+ unauthorized: You are not allowed to do that
212
309
  trek:
213
310
  dialog_component:
214
311
  text: This action cannot be undone.
@@ -241,9 +338,17 @@ en:
241
338
  list: List
242
339
  list_component:
243
340
  empty: No item.
341
+ password_strength:
342
+ fair: Fair
343
+ strong: Strong
344
+ too_short: Too short
345
+ weak: Weak
244
346
  search_component:
245
347
  reset: Reset search
246
348
  search: Search
349
+ user_agents:
350
+ browser_on_system: "%{browser} on %{system}"
351
+ unknown: Unknown browser
247
352
  user:
248
353
  locale:
249
354
  en: English
@@ -258,10 +363,10 @@ en:
258
363
  auto: auto
259
364
  dark: dark
260
365
  light: light
261
- admin_mailer:
262
- invite:
263
- subject: Your invitation
264
- notices:
265
- authentication_needed: Please log in
266
- rate_limited: Too many attempts, please try again later
267
- unauthorized: You are not allowed to do that
366
+ views:
367
+ pagination:
368
+ first: First
369
+ last: Last
370
+ next: Next
371
+ prev: Prev
372
+ truncate: "…"
@@ -1,20 +1,20 @@
1
1
  ---
2
2
  fr:
3
3
  activerecord:
4
- models:
5
- fragment:
6
- gender: m
7
- one: Fragment
8
- other: Fragments
9
- page:
10
- gender: f
11
- one: Page
12
- other: Pages
13
- user:
14
- gender: m_alt
15
- one: Utilisateur
16
- other: Utilisateurs
17
4
  attributes:
5
+ audit_log:
6
+ action: Action
7
+ actor: Auteur
8
+ auditable: Cible
9
+ created_at: Date
10
+ error_class: Erreur
11
+ http_method: Méthode
12
+ http_status: Statut
13
+ ip_address: Adresse IP
14
+ params: Paramètres
15
+ path: Chemin
16
+ request_id: Identifiant de requête
17
+ user_agent: Agent utilisateur
18
18
  fragment:
19
19
  content: Contenu
20
20
  key: Clé
@@ -30,6 +30,41 @@ fr:
30
30
  role: Rôle
31
31
  send_invite: Envoyer une invitation par e-mail
32
32
  theme: Thème
33
+ user/passkey:
34
+ name: Nom
35
+ errors:
36
+ models:
37
+ user:
38
+ attributes:
39
+ password:
40
+ common: fait partie des mots de passe les plus courants, choisissez-en un moins prévisible
41
+ digit_and_symbol: doit contenir un chiffre et un caractère spécial, sauf s'il fait au moins %{count} caractères
42
+ personal: ne doit pas contenir votre nom ni votre adresse e-mail
43
+ pwned: figure dans des fuites de données connues, choisissez-en un autre
44
+ recently_used:
45
+ one: est votre mot de passe actuel, choisissez-en un autre
46
+ other: fait partie de vos %{count} derniers mots de passe, choisissez-en un autre
47
+ sequence: ne doit pas contenir de suite comme 12345, azerty ou aaaaa
48
+ models:
49
+ audit_log:
50
+ gender: f
51
+ one: Entrée d'audit
52
+ other: Entrées d'audit
53
+ fragment:
54
+ gender: m
55
+ one: Fragment
56
+ other: Fragments
57
+ page:
58
+ gender: f
59
+ one: Page
60
+ other: Pages
61
+ user:
62
+ gender: m_alt
63
+ one: Utilisateur
64
+ other: Utilisateurs
65
+ user/passkey:
66
+ one: Clé d'accès
67
+ other: Clés d'accès
33
68
  admin:
34
69
  actions:
35
70
  all: Tout
@@ -40,27 +75,39 @@ fr:
40
75
  logout: Se déconnecter
41
76
  manage: Gérer
42
77
  new_resource:
43
- "n": Nouveau ou nouvelle %{resource}
44
78
  f: Nouvelle %{resource}
45
79
  m: Nouveau %{resource}
46
80
  m_alt: Nouvel %{resource}
81
+ "n": Nouveau ou nouvelle %{resource}
82
+ remove: Enlever
47
83
  reorder_resource:
48
- "n": Réorganiser les %{resources}
49
84
  f: Réorganiser les %{resources}
50
85
  m: Réorganiser les %{resources}
51
86
  m_alt: Réorganiser les %{resources}
52
- remove: Enlever
87
+ "n": Réorganiser les %{resources}
53
88
  replace: Remplacer
54
89
  search_resource:
55
- "n": Rechercher un·e %{resources}
56
90
  f: Rechercher une %{resource}
57
91
  m: Rechercher un %{resource}
58
92
  m_alt: Rechercher un %{resource}
59
- see_website: Voir le site
93
+ "n": Rechercher un·e %{resources}
60
94
  see_on_website: Voir sur le site
95
+ see_original: Voir l'original
96
+ see_website: Voir le site
61
97
  validate: Valider
62
98
  write: Écrire
99
+ active_sessions:
100
+ destroy:
101
+ success: Session déconnectée
102
+ destroy_others:
103
+ success: Déconnecté partout ailleurs
63
104
  attachments: Fichiers attachés
105
+ audit_logs:
106
+ index:
107
+ failures: Échecs (%{count})
108
+ intro: Modifications faites dans l'admin, par qui et sur quoi
109
+ show:
110
+ intro: Entrée du journal d'audit
64
111
  authentication:
65
112
  not_authenticated: Veuillez vous connecter
66
113
  not_authorized: Vous n'avez pas le droit d'effectuer cette action
@@ -68,8 +115,8 @@ fr:
68
115
  "false": Non
69
116
  "true": Oui
70
117
  brand:
71
- title: Espace d'administration
72
118
  subtitle: Trek v%{version}
119
+ title: Espace d'administration
73
120
  content: Contenu
74
121
  content_editor:
75
122
  link:
@@ -117,8 +164,8 @@ fr:
117
164
  intro: Fragment
118
165
  update:
119
166
  success: Fragment mis à jour
120
- navigation: Navigation
121
167
  metadata: Métadonnées
168
+ navigation: Navigation
122
169
  pages:
123
170
  create:
124
171
  success: Page ajoutée
@@ -158,6 +205,15 @@ fr:
158
205
  success: Action ajoutée
159
206
  form:
160
207
  title: Action
208
+ passkey_sessions:
209
+ create:
210
+ failure: La connexion avec cette clé d'accès n'a pas fonctionné, veuillez réessayer
211
+ passkeys:
212
+ create:
213
+ failure: La clé d'accès n'a pas pu être ajoutée, veuillez réessayer
214
+ success: Clé d'accès ajoutée
215
+ destroy:
216
+ success: Clé d'accès supprimée
161
217
  passwords_mailer:
162
218
  reset:
163
219
  subject: Nouveau mot de passe
@@ -175,21 +231,25 @@ fr:
175
231
  seo: SEO
176
232
  settings: Paramètres
177
233
  user_password_resets:
178
- not_found: Lien de réinitialisation de mot de passe invalide ou expiré.
179
234
  create:
180
- success: Un e-mail de récupération de mot de passe vous a été envoyé
235
+ success: Un lien pour choisir un nouveau mot de passe vous a été envoyé (sauf si aucun compte ne correspond à cette adresse e-mail)
236
+ edit:
237
+ rules: Au moins %{min} caractères avec un chiffre et un caractère spécial, ou au moins %{passphrase} caractères.
181
238
  new:
182
239
  remembered_password: J'ai retrouvé mon mot de passe !
240
+ not_found: Lien de réinitialisation de mot de passe invalide ou expiré.
183
241
  update:
184
242
  success: Votre mot de passe a été modifié
185
243
  user_sessions:
186
244
  create:
187
- failure: Erreur de connexion, vérifiez vos identifiants
188
- success: Vous êtes maintenant connecté·e
245
+ failure: Adresse e-mail ou mot de passe incorrect. Après plusieurs échecs d'affilée, la connexion est suspendue quelques minutes.
246
+ success: Vous êtes connecté·e, bienvenue !
189
247
  destroy:
190
248
  success: Vous êtes maintenant déconnecté·e
191
249
  new:
192
250
  forgot_password: J'ai oublié mon mot de passe
251
+ passkey: Se connecter avec une clé d'accès
252
+ passkey_failure: La connexion avec une clé d'accès n'a pas fonctionné, veuillez réessayer.
193
253
  users:
194
254
  create:
195
255
  success: Utilisateur ajouté
@@ -200,24 +260,61 @@ fr:
200
260
  intro: Mise à jour de l'utilisateur
201
261
  index:
202
262
  intro: Gestion des utilisateurs de l'espace d'administration
263
+ require_password_renewal: Demander un nouveau mot de passe à tous
264
+ require_password_renewal_confirm: Chaque utilisateur, vous compris, devra choisir un nouveau mot de passe à sa prochaine action. À utiliser en cas de compromission suspectée.
203
265
  new:
204
266
  intro: Ajout d'un utilisateur à l'espace d'administration
267
+ passkeys:
268
+ add: Ajouter une clé d'accès
269
+ added: Ajoutée le %{date}
270
+ already_added: Cet appareil ou ce gestionnaire de mots de passe a déjà une clé d'accès pour votre compte.
271
+ delete: Supprimer
272
+ delete_confirm: Vous ne pourrez plus vous connecter avec cette clé d'accès. Supprimez-la aussi de votre appareil ou de votre gestionnaire de mots de passe.
273
+ failure: La clé d'accès n'a pas pu être ajoutée, veuillez réessayer.
274
+ intro: Connectez-vous avec votre empreinte, votre visage ou le code de votre appareil plutôt qu'avec votre mot de passe. La clé d'accès reste sur votre appareil ou dans votre gestionnaire de mots de passe.
275
+ last_used: Utilisée il y a %{time}
276
+ never_used: Jamais utilisée
277
+ title: Se connecter sans mot de passe
278
+ require_password_renewal:
279
+ success: Chaque utilisateur devra choisir un nouveau mot de passe
205
280
  search:
206
281
  alert: Utilisateur non trouvé avec cette adresse e-mail
282
+ sessions:
283
+ change_password: Changez votre mot de passe
284
+ current: Ce navigateur
285
+ intro: Déconnectez ceux que vous ne reconnaissez pas.
286
+ last_activity: Dernière activité il y a %{time}
287
+ not_you_html: Ce n'était pas vous ? %{change_password}.
288
+ previous_sign_in: Connexion précédente
289
+ sign_out: Déconnecter
290
+ sign_out_others: Déconnecter partout ailleurs
291
+ sign_out_others_confirm: Tous les autres navigateurs connectés à votre compte seront déconnectés.
292
+ signed_in: Connexion le %{date}
293
+ title: Navigateurs actuellement connectés à ce compte
207
294
  show:
295
+ active_sessions: Sessions actives
208
296
  intro: Profil de l'utilisateur
209
- profil: Profil
297
+ passkeys: Clés d'accès
298
+ profile: Profil
210
299
  subscription: Abonnement
211
300
  update:
212
301
  success: Utilisateur mis à jour
213
- application_name: "Trek"
214
- views:
215
- pagination:
216
- first: Première page
217
- last: Dernière page
218
- next: Suivant
219
- prev: Précédent
220
- truncate: …
302
+ admin_mailer:
303
+ invite:
304
+ subject: Votre invitation
305
+ new_sign_in:
306
+ subject: Nouvelle connexion à votre compte
307
+ sign_in_locked:
308
+ subject: La connexion à votre compte est suspendue
309
+ application_name: Trek
310
+ errors:
311
+ messages:
312
+ disposable: est une adresse e-mail jetable, utilisez-en une permanente
313
+ notices:
314
+ authentication_needed: Veuillez vous connecter
315
+ password_renewal_needed: "Votre mot de passe doit être renouvelé : choisissez-en un nouveau"
316
+ rate_limited: Trop de tentatives, veuillez réessayer plus tard
317
+ unauthorized: Vous n'êtes pas autorisé·e à faire cela
221
318
  trek:
222
319
  dialog_component:
223
320
  text: Cette action ne peut pas être annulée.
@@ -250,9 +347,17 @@ fr:
250
347
  list: Liste
251
348
  list_component:
252
349
  empty: Aucun élément
350
+ password_strength:
351
+ fair: Moyen
352
+ strong: Fort
353
+ too_short: Trop court
354
+ weak: Faible
253
355
  search_component:
254
356
  reset: Réinitialiser la recherche
255
357
  search: Rechercher
358
+ user_agents:
359
+ browser_on_system: "%{browser} sur %{system}"
360
+ unknown: Navigateur inconnu
256
361
  user:
257
362
  locale:
258
363
  en: anglais
@@ -267,10 +372,10 @@ fr:
267
372
  auto: auto
268
373
  dark: sombre
269
374
  light: clair
270
- admin_mailer:
271
- invite:
272
- subject: Votre invitation
273
- notices:
274
- authentication_needed: Veuillez vous connecter
275
- rate_limited: Trop de tentatives, veuillez réessayer plus tard
276
- unauthorized: Vous n'êtes pas autorisé·e à faire cela
375
+ views:
376
+ pagination:
377
+ first: Première page
378
+ last: Dernière page
379
+ next: Suivant
380
+ prev: Précédent
381
+ truncate: "…"
@@ -53,7 +53,9 @@ export default defineConfig({
53
53
  text: 'Guides',
54
54
  items: [
55
55
  { text: 'How to customize Trek', link: '/customize' },
56
- { text: 'Deploying to Scalingo', link: '/deploy' }
56
+ { text: 'Deploying to Scalingo', link: '/deploy' },
57
+ { text: 'IP addresses behind a proxy', link: '/proxies' },
58
+ { text: 'Security and compliance', link: '/security' }
57
59
  ]
58
60
  },
59
61
  {
@@ -73,7 +75,9 @@ export default defineConfig({
73
75
  { text: 'Installer', link: '/reference/generators/install' },
74
76
  { text: 'Admin user', link: '/reference/generators/admin-user' },
75
77
  { text: 'Scaffold', link: '/reference/generators/scaffold' },
76
- { text: 'Taxonomies', link: '/reference/generators/taxonomies' }
78
+ { text: 'Taxonomies', link: '/reference/generators/taxonomies' },
79
+ { text: 'Audit log', link: '/reference/generators/audit-log' },
80
+ { text: 'Security headers', link: '/reference/generators/security-headers' }
77
81
  ]
78
82
  },
79
83
  {
@@ -120,7 +124,8 @@ export default defineConfig({
120
124
  { text: 'Forms', link: '/reference/forms' },
121
125
  { text: 'Formatters', link: '/reference/formatters' },
122
126
  { text: 'Policies', link: '/reference/policies' },
123
- { text: 'Uploaders', link: '/reference/uploaders' }
127
+ { text: 'Uploaders', link: '/reference/uploaders' },
128
+ { text: 'Validators', link: '/reference/validators' }
124
129
  ]
125
130
  },
126
131
  {
data/docs/deploy.md CHANGED
@@ -44,6 +44,8 @@ production:
44
44
 
45
45
  Provision a PostgreSQL addon (`scalingo --app <app> addons-add postgresql <plan>`), push, and the app boots with `RAILS_ENV`, `RAILS_LOG_TO_STDOUT` and `RAILS_SERVE_STATIC_FILES` already set by the buildpack.
46
46
 
47
+ Before going live, check what Trek expects from the hosting: backups, a shared cache store, the scheduled purges (see [Security and compliance](/security#what-trek-expects-from-the-hosting)).
48
+
47
49
  ## Options
48
50
 
49
51
  | Variable | Effect |
data/docs/proxies.md ADDED
@@ -0,0 +1,53 @@
1
+ # Visitors' IP addresses behind a proxy
2
+
3
+ Trek relies on `request.remote_ip`, the visitor's IP address as Rails works it out, in four places:
4
+
5
+ - the **active sessions** list, on each user's profile;
6
+ - the **audit log** (`ip_address`), when installed;
7
+ - the **rate limits** on signing in and on password reset requests (5 attempts in 3 minutes per address);
8
+ - the **admin's allowed addresses**, when set (see [below](#restricting-the-admin-to-ip-addresses)).
9
+
10
+ ## How Rails finds the address
11
+
12
+ Requests rarely reach the app directly: a hosting platform's load balancer or router forwards them, and adds the visitor's address to the `X-Forwarded-For` header. Rails only believes that header when it comes from a proxy it trusts, by default local and private addresses (`ActionDispatch::RemoteIp::TRUSTED_PROXIES`). Platform routers usually connect from such private addresses, so `remote_ip` is the visitor's.
13
+
14
+ ## Behind a CDN or a web application firewall
15
+
16
+ A CDN or a firewall in front of the app (Cloudflare, Imperva, Bunny…) adds another hop, from public addresses Rails doesn't trust. `remote_ip` is then the CDN's address for every request:
17
+
18
+ - every session and every audit log row shows the same IP;
19
+ - everyone shares one rate limit: a few failed sign-ins anywhere lock everyone out for 3 minutes.
20
+
21
+ List the CDN's address ranges, which it publishes, in `config/application.rb`:
22
+
23
+ ```ruby
24
+ # The CDN's ranges, from its documentation (e.g. https://www.cloudflare.com/ips/): without them,
25
+ # remote_ip is the CDN's address instead of the visitor's.
26
+ config.action_dispatch.trusted_proxies =
27
+ ActionDispatch::RemoteIp::TRUSTED_PROXIES + %w[
28
+ 203.0.113.0/24
29
+ 2001:db8::/32
30
+ ].map { |range| IPAddr.new(range) }
31
+ ```
32
+
33
+ Keep Rails' own `TRUSTED_PROXIES` in the list. Only trust ranges you know: a trusted proxy is believed about the address it forwards, so a range too wide would let anyone choose the IP Trek records. When the CDN changes its ranges, update the list.
34
+
35
+ ## Checking
36
+
37
+ Sign in from your own connection, then compare the address on your profile's **Active sessions** tab with the one a service such as `https://ifconfig.me` shows you. If it's your platform's or your CDN's address instead, add their ranges as above.
38
+
39
+ ## Passkeys
40
+
41
+ A passkey is tied to the site's domain, and the browser signs the origin it was used on. Trek takes both from the request (`request.host` and `request.base_url`), so Rails must see the address the visitor typed: the original `Host` header, and `https`. Most platforms forward them (`X-Forwarded-Host`, `X-Forwarded-Proto`). If adding a passkey or signing in with one fails behind a proxy that terminates TLS without saying so, set `config.assume_ssl = true` in `config/environments/production.rb`.
42
+
43
+ ## Restricting the admin to IP addresses
44
+
45
+ Some clients require the back office to be reachable from their network only. List the addresses and ranges allowed in `ADMIN_ALLOWED_IPS`, separated by commas or spaces:
46
+
47
+ ```sh
48
+ ADMIN_ALLOWED_IPS="203.0.113.0/24, 2001:db8::/32, 198.51.100.7"
49
+ ```
50
+
51
+ From anywhere else, `/admin` and everything below it, the sign-in page included, answers like an unknown URL: the app's 404 page, rather than a redirect to the sign-in page, so it doesn't even show there's an admin. The rest of the site is unaffected. Unset or empty, the admin is open to all, as by default. The variable is read when the app starts: restart it after a change. An entry that's neither an address nor a range stops it from starting.
52
+
53
+ `Trek::AdminIpAllowlist`, a middleware Trek's engine adds, checks `request.remote_ip`: behind a CDN or a firewall, add their ranges to `trusted_proxies` first ([above](#behind-a-cdn-or-a-web-application-firewall)), or every request seems to come from the CDN, and the admin is closed to everyone, or open to all if the CDN's range is allowed. Check the address Rails sees as [above](#checking), from an allowed network, before setting the variable.
@@ -20,6 +20,7 @@ The main building blocks, all in the `Trek::` namespace:
20
20
  | `PaginationComponent` | Kaminari-backed pagination |
21
21
  | `ToasterComponent` / `ToastComponent` | flash notifications |
22
22
  | `HeadingComponent` | headings |
23
+ | `SectionComponent` | a titled section of a page, with an optional hint under its heading |
23
24
  | `GateComponent` | authorization-based visibility gate |
24
25
  | `IconComponent` | SVG [icon](/reference/icons) renderer |
25
26
  | `Dashboard::SectionComponent` / `Dashboard::TileComponent` | dashboard sections of shortcut tiles |
@@ -75,7 +75,7 @@ with the chosen filter:
75
75
  ### Scopes
76
76
 
77
77
  `Trek::Scopes` filters by **named model scopes** rather than raw attributes. Declare the allowed scopes via
78
- `available_scopes`, then call `apply_scopes!` in your `index`. It also exposes `scope_counts` and
78
+ `available_scopes`, then call `apply_scopes!` in your `index`; any other name in `params[:scopes]` is ignored. It also exposes `scope_counts` and
79
79
  `unscoped_objects` helpers (the per-scope match counts and the pre-scope collection) for building scope tabs:
80
80
 
81
81
  ```ruby