masks-server 0.9.0 → 0.10.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 (84) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/masks/server/client_authentication.rb +3 -2
  3. data/app/controllers/masks/server/addresses_controller.rb +6 -1
  4. data/app/controllers/masks/server/application_controller.rb +29 -1
  5. data/app/controllers/masks/server/authorize_controller.rb +23 -18
  6. data/app/controllers/masks/server/device_verifications_controller.rb +1 -1
  7. data/app/controllers/masks/server/logins_controller.rb +2 -1
  8. data/app/controllers/masks/server/notifications_controller.rb +11 -1
  9. data/app/controllers/masks/server/profiles_controller.rb +5 -0
  10. data/app/controllers/masks/server/saml_controller.rb +1 -7
  11. data/app/controllers/masks/server/tokens_controller.rb +8 -0
  12. data/app/graphql/masks/server/manage/mutations/create_actor.rb +1 -0
  13. data/app/graphql/masks/server/manage/mutations/update_actor.rb +1 -0
  14. data/app/graphql/masks/server/manage/mutations/update_client.rb +4 -0
  15. data/app/graphql/masks/server/manage/types/client_type.rb +2 -0
  16. data/app/graphql/masks/server/manage/types/query_type.rb +7 -0
  17. data/app/jobs/masks/server/cleanup_job.rb +1 -0
  18. data/app/ldap/masks/server/ldap/listener.rb +16 -3
  19. data/app/logins/masks/server/login_states/confirmation.rb +9 -3
  20. data/app/logins/masks/server/login_states/signup.rb +1 -0
  21. data/app/models/masks/server/actor.rb +25 -2
  22. data/app/models/masks/server/client.rb +15 -0
  23. data/app/models/masks/server/client_keys.rb +12 -1
  24. data/app/models/masks/server/declared.rb +2 -1
  25. data/app/models/masks/server/event.rb +1 -0
  26. data/app/models/masks/server/notifications.rb +8 -0
  27. data/app/models/masks/server/pending_request.rb +8 -0
  28. data/app/models/masks/server/refresh_token.rb +8 -0
  29. data/app/models/masks/server/released_nickname.rb +26 -0
  30. data/app/models/masks/server/scim/filter.rb +45 -5
  31. data/app/models/masks/server/scim/user.rb +1 -0
  32. data/app/models/masks/server/session.rb +3 -4
  33. data/app/views/masks/server/account/_info.html.erb +12 -4
  34. data/app/views/masks/server/account/index.html.erb +1 -1
  35. data/app/views/masks/server/authorize/error.html.erb +2 -2
  36. data/config/locales/de/account.yml +4 -0
  37. data/config/locales/de/actors.yml +2 -0
  38. data/config/locales/de/authorize.yml +2 -0
  39. data/config/locales/de/events.yml +2 -0
  40. data/config/locales/de/flashes.yml +1 -0
  41. data/config/locales/de/logins.yml +1 -0
  42. data/config/locales/de/manage/client.yml +3 -0
  43. data/config/locales/de/manage/ldap.yml +1 -0
  44. data/config/locales/de/notifications.yml +1 -0
  45. data/config/locales/en/account.yml +4 -0
  46. data/config/locales/en/actors.yml +2 -0
  47. data/config/locales/en/authorize.yml +2 -0
  48. data/config/locales/en/events.yml +2 -0
  49. data/config/locales/en/flashes.yml +1 -0
  50. data/config/locales/en/logins.yml +1 -0
  51. data/config/locales/en/manage/client.yml +3 -0
  52. data/config/locales/en/manage/ldap.yml +1 -0
  53. data/config/locales/en/notifications.yml +1 -0
  54. data/config/locales/es/account.yml +4 -0
  55. data/config/locales/es/actors.yml +2 -0
  56. data/config/locales/es/authorize.yml +2 -0
  57. data/config/locales/es/events.yml +2 -0
  58. data/config/locales/es/flashes.yml +1 -0
  59. data/config/locales/es/logins.yml +1 -0
  60. data/config/locales/es/manage/client.yml +3 -0
  61. data/config/locales/es/manage/ldap.yml +1 -0
  62. data/config/locales/es/notifications.yml +1 -0
  63. data/config/locales/fr/account.yml +4 -0
  64. data/config/locales/fr/actors.yml +2 -0
  65. data/config/locales/fr/authorize.yml +2 -0
  66. data/config/locales/fr/events.yml +2 -0
  67. data/config/locales/fr/flashes.yml +1 -0
  68. data/config/locales/fr/logins.yml +1 -0
  69. data/config/locales/fr/manage/client.yml +3 -0
  70. data/config/locales/fr/manage/ldap.yml +1 -0
  71. data/config/locales/fr/notifications.yml +1 -0
  72. data/config/routes.rb +1 -0
  73. data/db/migrate/20261018100000_create_released_nicknames.rb +22 -0
  74. data/db/migrate/20261018110000_add_refresh_token_absolute_lifetime_to_clients.rb +5 -0
  75. data/lib/masks/server/configuration.rb +8 -0
  76. data/lib/masks/server/tenancy/middleware.rb +9 -1
  77. data/public/masks-assets/.vite/manifest.json +2 -2
  78. data/public/masks-assets/assets/{app-B27oPUll.css → app-D7W0gAyT.css} +1 -1
  79. data/public/masks-assets/assets/manage-DR9qQiBU.js +569 -0
  80. data/public/masks-assets/assets/manage-DR9qQiBU.js.map +1 -0
  81. data/version.txt +1 -1
  82. metadata +7 -4
  83. data/public/masks-assets/assets/manage-Dbfiy3Mj.js +0 -567
  84. data/public/masks-assets/assets/manage-Dbfiy3Mj.js.map +0 -1
@@ -5,6 +5,14 @@ module Masks
5
5
  30.days
6
6
  end
7
7
 
8
+ def self.absolute_lifetime
9
+ 90.days
10
+ end
11
+
12
+ def exhausted?(limit)
13
+ root.created_at + limit <= Time.current
14
+ end
15
+
8
16
  def self.mint!(**attributes)
9
17
  super.tap { |token| token.actor&.active! }
10
18
  end
@@ -0,0 +1,26 @@
1
+ module Masks
2
+ module Server
3
+ class ReleasedNickname < ApplicationRecord
4
+ include TenantScoped
5
+
6
+ HOLD = 30.days
7
+
8
+ belongs_to :actor, optional: true
9
+
10
+ scope :holding, ->(nickname) { where(nickname: nickname.to_s.downcase).where("released_at > ?", HOLD.ago) }
11
+ scope :lapsed, -> { where(released_at: ...HOLD.ago) }
12
+
13
+ class << self
14
+ def release!(nickname, actor:)
15
+ return if nickname.blank?
16
+
17
+ find_or_initialize_by(nickname: nickname.downcase).update!(actor: actor, released_at: Time.current)
18
+ end
19
+
20
+ def held_from?(nickname, actor)
21
+ holding(nickname).where.not(actor_id: actor.id).or(holding(nickname).where(actor_id: nil)).exists?
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
@@ -1,8 +1,14 @@
1
+ require "strscan"
2
+
1
3
  module Masks
2
4
  module Server
3
5
  module Scim
4
6
  class Filter
5
- COMPARISON = /\A\s*([A-Za-z][\w.:\[\]" -]*?)\s+(eq|ne|co|sw|ew|pr|gt|ge|lt|le)(?:\s+("(?:[^"\\]|\\.)*"|true|false|null|[\d.]+))?\s*\z/i
7
+ LONGEST = 512
8
+ TOKEN = /"(?:[^"\\]|\\.)*"|(?:[^\s"\[]|\[[^\]]*\])+/
9
+ PATH = /\A[A-Za-z][\w.:\[\]" ]*\z/
10
+ VALUE = /\A(?:"(?:[^"\\]|\\.)*"|true|false|null|[\d.]+)\z/
11
+ OPERATORS = %w[eq ne co sw ew pr gt ge lt le].freeze
6
12
  ATTRIBUTES = {
7
13
  "id" => "actors.uuid",
8
14
  "username" => :user_name,
@@ -30,7 +36,9 @@ module Masks
30
36
  end
31
37
 
32
38
  def apply(relation)
33
- clauses = @expression.split(/\s+and\s+/i)
39
+ raise Error.new(:bad_request, "a filter is at most #{LONGEST} characters", scim_type: "invalidFilter") if @expression.length > LONGEST
40
+
41
+ clauses = clauses_of(tokens)
34
42
 
35
43
  raise Error.new(:bad_request, "at most four comparisons joined by and are understood", scim_type: "invalidFilter") if clauses.size > 4
36
44
 
@@ -39,12 +47,44 @@ module Masks
39
47
 
40
48
  private
41
49
 
50
+ def tokens
51
+ scanner = StringScanner.new(@expression)
52
+ held = []
53
+
54
+ until scanner.eos?
55
+ next if scanner.skip(/\s+/)
56
+
57
+ token = scanner.scan(TOKEN)
58
+
59
+ raise unreadable(@expression) if token.nil?
60
+
61
+ held << token
62
+ end
63
+
64
+ held
65
+ end
66
+
67
+ def clauses_of(tokens)
68
+ clauses = [ [] ]
69
+
70
+ tokens.each do |token|
71
+ token.casecmp?("and") ? clauses << [] : clauses.last << token
72
+ end
73
+
74
+ clauses
75
+ end
76
+
77
+ def unreadable(clause)
78
+ Error.new(:bad_request, "#{clause.strip} is not a filter this server reads", scim_type: "invalidFilter")
79
+ end
80
+
42
81
  def compare(relation, clause)
43
- match = COMPARISON.match(clause)
82
+ path, operator, raw = clause
44
83
 
45
- raise Error.new(:bad_request, "#{clause.strip} is not a filter this server reads", scim_type: "invalidFilter") if match.nil?
84
+ raise unreadable(clause.join(" ")) unless clause.size.between?(2, 3)
85
+ raise unreadable(clause.join(" ")) unless PATH.match?(path) && OPERATORS.include?(operator.downcase)
86
+ raise unreadable(clause.join(" ")) unless raw.nil? || VALUE.match?(raw)
46
87
 
47
- path, operator, raw = match.captures
48
88
  key = normalized(path)
49
89
  column = @columns[key]
50
90
  operator = operator.downcase
@@ -118,6 +118,7 @@ module Masks
118
118
  raise Error.new(:bad_request, "userName is required", scim_type: "invalidValue") if value.blank?
119
119
 
120
120
  held = value.to_s.strip
121
+ actor.trusted_rename = true
121
122
 
122
123
  if held.match?(URI::MailTo::EMAIL_REGEXP)
123
124
  actor.email = held
@@ -72,11 +72,10 @@ module Masks
72
72
  end
73
73
 
74
74
  def first_factored!(held, at: nil)
75
- changes = {}
76
- changes[:authenticated_at] = at if at.present? && (authenticated_at.nil? || at > authenticated_at)
77
- changes.merge!(first_factor: held["factor"], provider_id: held["provider_id"]) if held["factor"].present? && held != first_factored_by
75
+ self.authenticated_at = at if at && at > authenticated_at
76
+ assign_attributes(first_factor: held["factor"], provider_id: held["provider_id"]) if held["factor"].present?
78
77
 
79
- update!(changes) if changes.any?
78
+ save! if changed?
80
79
  end
81
80
 
82
81
  def bound?
@@ -46,7 +46,11 @@
46
46
  </summary>
47
47
 
48
48
  <div class="entry-body">
49
- <p class="aside"><%= account_text("notifications.lede") %></p>
49
+ <p class="aside"><%= account_text("notifications.lede") %> <%= account_text("notifications.kept") %></p>
50
+
51
+ <% unless fresh %>
52
+ <p class="aside"><%= account_text("notifications.again") %></p>
53
+ <% end %>
50
54
 
51
55
  <% unless Masks::Server::Notifications.mailable?(actor) %>
52
56
  <p class="aside aside-bad"><%= account_text("notifications.silent") %></p>
@@ -63,8 +67,12 @@
63
67
  <% actions.each do |action| %>
64
68
  <label class="switch">
65
69
  <span><%= t("events.actions.#{action}") %></span>
66
- <%= check_box_tag "notifications[]", action, actor.notified?(action),
67
- id: "notify-#{action.tr('.', '-')}" %>
70
+ <% if Masks::Server::Notifications.always?(action) %>
71
+ <span class="switch-kept"><%= account_text("notifications.always") %></span>
72
+ <% else %>
73
+ <%= check_box_tag "notifications[]", action, actor.notified?(action),
74
+ id: "notify-#{action.tr('.', '-')}" %>
75
+ <% end %>
68
76
  </label>
69
77
  <% end %>
70
78
  </div>
@@ -72,7 +80,7 @@
72
80
  <% end %>
73
81
 
74
82
  <div class="action-row">
75
- <button type="submit" class="action action-sm"><%= account_text("notifications.submit") %></button>
83
+ <button type="submit" class="action action-sm"><%= account_text(fresh ? "notifications.submit" : "notifications.submit_again") %></button>
76
84
  </div>
77
85
  <% end %>
78
86
  </div>
@@ -31,7 +31,7 @@
31
31
 
32
32
  <%= render "masks/server/account/apps", apps: @apps, connections: @connections, linkable: @linkable %>
33
33
 
34
- <%= render "masks/server/account/info", actor: @actor, directed: @directed %>
34
+ <%= render "masks/server/account/info", actor: @actor, directed: @directed, fresh: @fresh %>
35
35
 
36
36
  <%= render "masks/server/account/activity", events: @events %>
37
37
 
@@ -4,9 +4,9 @@
4
4
  <div id="authorize-error" class="flow" data-error="<%= @error_code %>">
5
5
  <span class="state state-bad"><%= t(".stopped") %></span>
6
6
 
7
- <h1 class="prompt-title"><%= t(".heading") %></h1>
7
+ <h1 class="prompt-title"><%= @refused ? t(".refused") : t(".heading") %></h1>
8
8
 
9
9
  <p class="said"><%= @error_code %> — <%= @error_description %></p>
10
10
 
11
- <p class="prompt-lede"><%= t(".contact") %></p>
11
+ <p class="prompt-lede"><%= @refused ? t(".nothing_shared") : t(".contact") %></p>
12
12
  </div>
@@ -251,6 +251,10 @@ de:
251
251
  lede: "Alles hier ist aktiv, bis Sie es deaktivieren. Diese Nachrichten kommen, sobald etwas passiert, und auf keine kann geantwortet werden."
252
252
  silent: "Es wird nichts gesendet, weil diese Nachrichten nur an eine bestätigte E-Mail-Adresse gehen."
253
253
  submit: "Speichern"
254
+ kept: "Ein geändertes oder zurückgesetztes Passwort und eine riskante Anmeldung werden immer gemeldet."
255
+ always: "Immer"
256
+ again: "Ihre letzte Anmeldung ist eine Weile her, daher werden Sie zuerst gebeten, sich erneut anzumelden."
257
+ submit_again: "Erneut anmelden"
254
258
 
255
259
  export:
256
260
  heading: "Ihr Konto herunterladen"
@@ -6,3 +6,5 @@ de:
6
6
  attributes:
7
7
  base:
8
8
  unnamed: "Ein Konto braucht einen Spitznamen oder eine E-Mail-Adresse."
9
+ nickname:
10
+ held: "wurde vor Kurzem freigegeben und kann noch nicht übernommen werden"
@@ -5,3 +5,5 @@ de:
5
5
  stopped: "Angehalten"
6
6
  heading: "Die App hat etwas angefordert, das dieser Server nicht tut."
7
7
  contact: "Es wurde nichts geteilt. Die App muss das beheben."
8
+ refused: "Diese Anmeldung wurde abgelehnt."
9
+ nothing_shared: "Mit der App wurde nichts geteilt. Sie können diese Seite schließen."
@@ -6,6 +6,8 @@ de:
6
6
  ended: "Abgemeldet"
7
7
  revoked: "Sitzung widerrufen"
8
8
  expired: "Sitzung abgelaufen"
9
+ notifications:
10
+ changed: "Benachrichtigungen geändert"
9
11
  logout:
10
12
  undelivered: "Abmeldung nicht an eine App zugestellt"
11
13
  login:
@@ -129,6 +129,7 @@ de:
129
129
  directed: "Das Verzeichnis Ihrer Organisation verwaltet Ihren Namen."
130
130
  invalid: "%{errors}"
131
131
  updated: "Ihre Angaben wurden gespeichert."
132
+ again: "Melden Sie sich erneut an, um Ihren Spitznamen zu ändern. Ihre letzte Anmeldung ist eine Weile her."
132
133
 
133
134
  verifications:
134
135
  too_many_requests: "Zu viele Anfragen. Warten Sie einige Minuten."
@@ -238,6 +238,7 @@ de:
238
238
  too-many-attempts: "Zu viele Versuche. Warten Sie einige Minuten."
239
239
  too-many-attempts-for-account: "Zu viele Versuche für dieses Konto."
240
240
  too-many-codes: "An diese Adresse wurden zu viele Codes gesendet. Warten Sie einige Minuten."
241
+ too-many-texts: "An diese Nummer wurden zu viele Codes gesendet. Warten Sie einige Minuten."
241
242
  prove-email-first: "Geben Sie zuerst den an diese Adresse gesendeten Code ein."
242
243
  no-account-for-email: "Kein Konto hier verwendet diese Adresse."
243
244
  invalid-setup-token: "Dieses Einrichtungstoken ist falsch."
@@ -115,6 +115,8 @@ de:
115
115
  sector_placeholder: "nötig, wenn seine Redirect-URIs mehrere Hosts umfassen"
116
116
  unattended: "Meldet sich als er selbst an (client_credentials)"
117
117
  rotate: "Secret rotieren"
118
+ secret_expires: "Das Secret läuft am %{day} ab."
119
+ secret_expired: "Das Secret ist am %{day} abgelaufen. Rotieren Sie es, sonst kann sich nichts mehr damit anmelden."
118
120
  archive: "Archivieren"
119
121
  saml:
120
122
  title: "SAML"
@@ -146,6 +148,7 @@ de:
146
148
  consent: "Akteure um Zustimmung bitten"
147
149
  access: "Access-Tokens gelten (Minuten, leer für eine Stunde)"
148
150
  refresh: "Refresh-Tokens gelten ungenutzt (Tage, leer für 30)"
151
+ refresh_absolute: "Refresh-Tokens enden nach der Anmeldung (Tage, leer für 90)"
149
152
  consent_lifetime: "Zustimmung gilt (Tage, leer bis zum Widerruf)"
150
153
  logout: "Back-Channel-Logout-URI"
151
154
  logout_session: "Sein Logout-Token benennt die Sitzung (sid)"
@@ -17,6 +17,7 @@ de:
17
17
  address: "Adresse"
18
18
  port: "Port %{port}"
19
19
  ldaps: "Port %{port}, LDAPS"
20
+ local_only: "Ohne Zertifikat antwortet das Verzeichnis nur auf dem eigenen Rechner dieses Servers, damit kein Passwort unverschlüsselt über das Netz geht. Setzen Sie MASKS_LDAP_CERT und MASKS_LDAP_KEY, um LDAPS auf jeder Adresse anzubieten, oder MASKS_LDAP_PLAINTEXT=true in einem privaten Netz, dem Sie vertrauen."
20
21
  label: "Bezeichnung"
21
22
  issue: "Zugang ausstellen"
22
23
  shown_once: "Dieses Geheimnis wird nur einmal angezeigt. Kopieren Sie es jetzt."
@@ -5,6 +5,7 @@ de:
5
5
 
6
6
  notifications:
7
7
  saved: "Gespeichert."
8
+ again: "Melden Sie sich erneut an, um zu ändern, welche E-Mails Sie erhalten. Ihre letzte Anmeldung ist eine Weile her."
8
9
 
9
10
  groups:
10
11
  password: "Passwort"
@@ -251,6 +251,10 @@ en:
251
251
  lede: "Everything here is on until you turn it off. These arrive as they happen, and none of them can be replied to."
252
252
  silent: "Nothing is being sent, because these only go to a confirmed email address."
253
253
  submit: "Save"
254
+ kept: "A changed or reset password and a risky sign-in are always announced."
255
+ always: "Always"
256
+ again: "You signed in a while ago, so you will be asked to sign in again first."
257
+ submit_again: "Sign in again"
254
258
 
255
259
  export:
256
260
  heading: "Download your account"
@@ -6,3 +6,5 @@ en:
6
6
  attributes:
7
7
  base:
8
8
  unnamed: "An account needs a nickname or an email address."
9
+ nickname:
10
+ held: "was given up recently and cannot be taken yet"
@@ -5,3 +5,5 @@ en:
5
5
  stopped: "Stopped"
6
6
  heading: "The app asked for something this server will not do."
7
7
  contact: "Nothing was shared. The app has to fix this."
8
+ refused: "This sign-in was refused."
9
+ nothing_shared: "Nothing was shared with the app. You can close this page."
@@ -6,6 +6,8 @@ en:
6
6
  ended: "Signed out"
7
7
  revoked: "Session revoked"
8
8
  expired: "Session timed out"
9
+ notifications:
10
+ changed: "Notifications changed"
9
11
  logout:
10
12
  undelivered: "Sign-out not delivered to an app"
11
13
  login:
@@ -129,6 +129,7 @@ en:
129
129
  directed: "Your organization's directory manages your name."
130
130
  invalid: "%{errors}"
131
131
  updated: "Your details have been saved."
132
+ again: "Sign in again to change your nickname. It has been a while since you did."
132
133
 
133
134
  verifications:
134
135
  too_many_requests: "Too many requests. Wait a few minutes."
@@ -238,6 +238,7 @@ en:
238
238
  too-many-attempts: "Too many attempts. Wait a few minutes."
239
239
  too-many-attempts-for-account: "Too many attempts for that account."
240
240
  too-many-codes: "Too many codes have gone to that address. Wait a few minutes."
241
+ too-many-texts: "Too many codes have gone to that number. Wait a few minutes."
241
242
  prove-email-first: "Enter the code sent to that address first."
242
243
  no-account-for-email: "No account here uses that address."
243
244
  invalid-setup-token: "That setup token is wrong."
@@ -115,6 +115,8 @@ en:
115
115
  sector_placeholder: "needed when its redirect URIs span several hosts"
116
116
  unattended: "Signs in as itself (client_credentials)"
117
117
  rotate: "Rotate secret"
118
+ secret_expires: "The secret expires on %{day}."
119
+ secret_expired: "The secret expired on %{day}. Rotate it, or nothing can sign in with it."
118
120
  archive: "Archive"
119
121
  saml:
120
122
  title: "SAML"
@@ -146,6 +148,7 @@ en:
146
148
  consent: "Ask actors to consent"
147
149
  access: "Access tokens last (minutes, blank for an hour)"
148
150
  refresh: "Refresh tokens last unused (days, blank for 30)"
151
+ refresh_absolute: "Refresh tokens stop after a sign-in (days, blank for 90)"
149
152
  consent_lifetime: "Consent lasts (days, blank until revoked)"
150
153
  logout: "Back-channel logout URI"
151
154
  logout_session: "Its logout token names the session (sid)"
@@ -17,6 +17,7 @@ en:
17
17
  address: "Address"
18
18
  port: "Port %{port}"
19
19
  ldaps: "Port %{port}, LDAPS"
20
+ local_only: "Without a certificate, the directory answers only on this server's own machine, so no password crosses the network in the clear. Set MASKS_LDAP_CERT and MASKS_LDAP_KEY to serve LDAPS on every address, or MASKS_LDAP_PLAINTEXT=true on a private network you trust."
20
21
  label: "Label"
21
22
  issue: "Issue a credential"
22
23
  shown_once: "This secret is shown once. Copy it now."
@@ -5,6 +5,7 @@ en:
5
5
 
6
6
  notifications:
7
7
  saved: "Saved."
8
+ again: "Sign in again to change which emails you get. It has been a while since you did."
8
9
 
9
10
  groups:
10
11
  password: "Password"
@@ -251,6 +251,10 @@ es:
251
251
  lede: "Todo lo de aquí está activado hasta que lo desactives. Llegan en el momento en que ocurre y no se puede responder a ninguno."
252
252
  silent: "No se envía nada, porque solo van a una dirección de correo confirmada."
253
253
  submit: "Guardar"
254
+ kept: "Una contraseña cambiada o restablecida y un inicio de sesión de riesgo se avisan siempre."
255
+ always: "Siempre"
256
+ again: "Hace tiempo que iniciaste sesión, así que primero se te pedirá que vuelvas a iniciarla."
257
+ submit_again: "Volver a iniciar sesión"
254
258
 
255
259
  export:
256
260
  heading: "Descargar tu cuenta"
@@ -6,3 +6,5 @@ es:
6
6
  attributes:
7
7
  base:
8
8
  unnamed: "Una cuenta necesita un apodo o una dirección de correo."
9
+ nickname:
10
+ held: "se liberó hace poco y todavía no se puede usar"
@@ -5,3 +5,5 @@ es:
5
5
  stopped: "Detenida"
6
6
  heading: "La aplicación pidió algo que este servidor no hará."
7
7
  contact: "No se compartió nada. La aplicación tiene que corregirlo."
8
+ refused: "Este inicio de sesión fue rechazado."
9
+ nothing_shared: "No se compartió nada con la aplicación. Puede cerrar esta página."
@@ -6,6 +6,8 @@ es:
6
6
  ended: "Cerró sesión"
7
7
  revoked: "Sesión revocada"
8
8
  expired: "Sesión caducada"
9
+ notifications:
10
+ changed: "Notificaciones cambiadas"
9
11
  logout:
10
12
  undelivered: "Cierre de sesión no entregado a una aplicación"
11
13
  login:
@@ -129,6 +129,7 @@ es:
129
129
  directed: "El directorio de tu organización gestiona tu nombre."
130
130
  invalid: "%{errors}"
131
131
  updated: "Se guardaron tus datos."
132
+ again: "Vuelve a iniciar sesión para cambiar tu apodo. Hace tiempo que no lo haces."
132
133
 
133
134
  verifications:
134
135
  too_many_requests: "Demasiadas solicitudes. Espera unos minutos."
@@ -238,6 +238,7 @@ es:
238
238
  too-many-attempts: "Demasiados intentos. Espera unos minutos."
239
239
  too-many-attempts-for-account: "Demasiados intentos para esa cuenta."
240
240
  too-many-codes: "Se enviaron demasiados códigos a esa dirección. Espera unos minutos."
241
+ too-many-texts: "Se enviaron demasiados códigos a ese número. Espera unos minutos."
241
242
  prove-email-first: "Primero escribe el código enviado a esa dirección."
242
243
  no-account-for-email: "Ninguna cuenta de aquí usa esa dirección."
243
244
  invalid-setup-token: "Ese token de configuración no es correcto."
@@ -115,6 +115,8 @@ es:
115
115
  sector_placeholder: "necesario cuando sus URI de redirección abarcan varios hosts"
116
116
  unattended: "Inicia sesión por sí mismo (client_credentials)"
117
117
  rotate: "Rotar secreto"
118
+ secret_expires: "El secreto caduca el %{day}."
119
+ secret_expired: "El secreto caducó el %{day}. Rótalo, o nada podrá iniciar sesión con él."
118
120
  archive: "Archivar"
119
121
  saml:
120
122
  title: "SAML"
@@ -146,6 +148,7 @@ es:
146
148
  consent: "Pedir el consentimiento de los actores"
147
149
  access: "Los tokens de acceso duran (minutos, vacío para una hora)"
148
150
  refresh: "Los tokens de actualización duran sin usarse (días, vacío para 30)"
151
+ refresh_absolute: "Los tokens de actualización terminan tras el inicio de sesión (días, vacío para 90)"
149
152
  consent_lifetime: "El consentimiento dura (días, vacío hasta que se revoque)"
150
153
  logout: "URI de cierre de sesión por back-channel"
151
154
  logout_session: "Su token de cierre de sesión nombra la sesión (sid)"
@@ -17,6 +17,7 @@ es:
17
17
  address: "Dirección"
18
18
  port: "Puerto %{port}"
19
19
  ldaps: "Puerto %{port}, LDAPS"
20
+ local_only: "Sin certificado, el directorio responde solo en la propia máquina de este servidor, para que ninguna contraseña cruce la red sin cifrar. Defina MASKS_LDAP_CERT y MASKS_LDAP_KEY para servir LDAPS en todas las direcciones, o MASKS_LDAP_PLAINTEXT=true en una red privada de confianza."
20
21
  label: "Etiqueta"
21
22
  issue: "Emitir una credencial"
22
23
  shown_once: "Este secreto se muestra una sola vez. Cópialo ahora."
@@ -5,6 +5,7 @@ es:
5
5
 
6
6
  notifications:
7
7
  saved: "Guardado."
8
+ again: "Vuelve a iniciar sesión para cambiar qué correos recibes. Hace tiempo que no lo haces."
8
9
 
9
10
  groups:
10
11
  password: "Contraseña"
@@ -251,6 +251,10 @@ fr:
251
251
  lede: "Tout ce qui figure ici est activé jusqu’à ce que vous le désactiviez. Ces e-mails arrivent au fil des événements, et il n’est pas possible d’y répondre."
252
252
  silent: "Rien n’est envoyé, car ces e-mails ne partent que vers une adresse e-mail confirmée."
253
253
  submit: "Enregistrer"
254
+ kept: "Un mot de passe modifié ou réinitialisé et une connexion à risque sont toujours signalés."
255
+ always: "Toujours"
256
+ again: "Votre dernière connexion remonte à un moment, vous devrez donc d’abord vous reconnecter."
257
+ submit_again: "Se reconnecter"
254
258
 
255
259
  export:
256
260
  heading: "Télécharger votre compte"
@@ -6,3 +6,5 @@ fr:
6
6
  attributes:
7
7
  base:
8
8
  unnamed: "Un compte doit avoir un pseudo ou une adresse e-mail."
9
+ nickname:
10
+ held: "a été libéré récemment et ne peut pas encore être repris"
@@ -5,3 +5,5 @@ fr:
5
5
  stopped: "Interrompu"
6
6
  heading: "L’application a demandé quelque chose que ce serveur refuse de faire."
7
7
  contact: "Rien n’a été partagé. C’est à l’application de corriger le problème."
8
+ refused: "Cette connexion a été refusée."
9
+ nothing_shared: "Rien n’a été partagé avec l’application. Vous pouvez fermer cette page."
@@ -6,6 +6,8 @@ fr:
6
6
  ended: "Déconnexion"
7
7
  revoked: "Session révoquée"
8
8
  expired: "Session expirée"
9
+ notifications:
10
+ changed: "Notifications modifiées"
9
11
  logout:
10
12
  undelivered: "Déconnexion non transmise à une application"
11
13
  login:
@@ -129,6 +129,7 @@ fr:
129
129
  directed: "L’annuaire de votre organisation gère votre nom."
130
130
  invalid: "%{errors}"
131
131
  updated: "Vos informations ont été enregistrées."
132
+ again: "Reconnectez-vous pour changer votre pseudo. Votre dernière connexion remonte à un moment."
132
133
 
133
134
  verifications:
134
135
  too_many_requests: "Trop de demandes. Attendez quelques minutes."
@@ -238,6 +238,7 @@ fr:
238
238
  too-many-attempts: "Trop de tentatives. Patientez quelques minutes."
239
239
  too-many-attempts-for-account: "Trop de tentatives pour ce compte."
240
240
  too-many-codes: "Trop de codes ont été envoyés à cette adresse. Patientez quelques minutes."
241
+ too-many-texts: "Trop de codes ont été envoyés à ce numéro. Patientez quelques minutes."
241
242
  prove-email-first: "Saisissez d’abord le code envoyé à cette adresse."
242
243
  no-account-for-email: "Aucun compte ici n’utilise cette adresse."
243
244
  invalid-setup-token: "Ce jeton de configuration est incorrect."
@@ -115,6 +115,8 @@ fr:
115
115
  sector_placeholder: "nécessaire quand ses URI de redirection couvrent plusieurs hôtes"
116
116
  unattended: "Se connecte en son propre nom (client_credentials)"
117
117
  rotate: "Renouveler le secret"
118
+ secret_expires: "Le secret expire le %{day}."
119
+ secret_expired: "Le secret a expiré le %{day}. Renouvelez-le, sinon rien ne peut se connecter avec."
118
120
  archive: "Archiver"
119
121
  saml:
120
122
  title: "SAML"
@@ -146,6 +148,7 @@ fr:
146
148
  consent: "Demander le consentement des acteurs"
147
149
  access: "Durée des jetons d’accès (minutes, vide pour une heure)"
148
150
  refresh: "Durée des jetons d’actualisation sans utilisation (jours, vide pour 30)"
151
+ refresh_absolute: "Fin des jetons d’actualisation après la connexion (jours, vide pour 90)"
149
152
  consent_lifetime: "Durée du consentement (jours, vide jusqu’à révocation)"
150
153
  logout: "URI de déconnexion back-channel"
151
154
  logout_session: "Son jeton de déconnexion indique la session (sid)"
@@ -17,6 +17,7 @@ fr:
17
17
  address: "Adresse"
18
18
  port: "Port %{port}"
19
19
  ldaps: "Port %{port}, LDAPS"
20
+ local_only: "Sans certificat, l’annuaire ne répond que sur la machine de ce serveur, pour qu’aucun mot de passe ne traverse le réseau en clair. Définissez MASKS_LDAP_CERT et MASKS_LDAP_KEY pour servir LDAPS sur toutes les adresses, ou MASKS_LDAP_PLAINTEXT=true sur un réseau privé de confiance."
20
21
  label: "Libellé"
21
22
  issue: "Émettre un identifiant"
22
23
  shown_once: "Ce secret n'est affiché qu'une fois. Copiez-le maintenant."
@@ -5,6 +5,7 @@ fr:
5
5
 
6
6
  notifications:
7
7
  saved: "Enregistré."
8
+ again: "Reconnectez-vous pour changer les e-mails que vous recevez. Votre dernière connexion remonte à un moment."
8
9
 
9
10
  groups:
10
11
  password: "Mot de passe"
data/config/routes.rb CHANGED
@@ -18,6 +18,7 @@ Masks::Server::Engine.routes.draw do
18
18
  post "/ssf/events", to: "received_signals#create", as: :received_signals
19
19
 
20
20
  match "/authorize", to: "authorize#show", via: %i[get post], as: :authorize
21
+ get "/authorize/refused", to: "authorize#refused", as: :authorize_refused
21
22
  post "/par", to: "pushed_authorizations#create", as: :pushed_authorization
22
23
  post "/device_authorization", to: "device_authorizations#create", as: :device_authorization
23
24
 
@@ -0,0 +1,22 @@
1
+ class CreateReleasedNicknames < ActiveRecord::Migration[8.1]
2
+ include Masks::Server::TenantIsolation
3
+
4
+ def up
5
+ create_table :released_nicknames do |t|
6
+ t.references :tenant, null: false, foreign_key: { on_delete: :cascade }, index: false
7
+ t.references :actor, foreign_key: { on_delete: :nullify }, index: false
8
+ t.string :nickname, null: false
9
+ t.datetime :released_at, null: false
10
+ end
11
+
12
+ add_index :released_nicknames, %i[tenant_id nickname], unique: true
13
+ add_index :released_nicknames, :released_at
14
+
15
+ enable_row_level_security(:released_nicknames)
16
+ end
17
+
18
+ def down
19
+ disable_row_level_security(:released_nicknames)
20
+ drop_table :released_nicknames
21
+ end
22
+ end
@@ -0,0 +1,5 @@
1
+ class AddRefreshTokenAbsoluteLifetimeToClients < ActiveRecord::Migration[8.1]
2
+ def change
3
+ add_column :clients, :refresh_token_absolute_lifetime, :integer
4
+ end
5
+ end