masks-server 0.10.0 → 0.12.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 (183) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/masks/server/account_controller.rb +3 -0
  3. data/app/controllers/masks/server/app_passwords_controller.rb +41 -0
  4. data/app/controllers/masks/server/authorize_controller.rb +33 -2
  5. data/app/controllers/masks/server/discovery_controller.rb +36 -0
  6. data/app/controllers/masks/server/enrollments_controller.rb +17 -0
  7. data/app/controllers/masks/server/forward_auth_controller.rb +104 -12
  8. data/app/controllers/masks/server/logins_controller.rb +4 -3
  9. data/app/controllers/masks/server/organization_access_controller.rb +39 -0
  10. data/app/controllers/masks/server/organization_members_controller.rb +3 -1
  11. data/app/graphql/masks/server/manage/mutations/base_mutation.rb +7 -0
  12. data/app/graphql/masks/server/manage/mutations/create_group.rb +6 -4
  13. data/app/graphql/masks/server/manage/mutations/decline_access.rb +27 -0
  14. data/app/graphql/masks/server/manage/mutations/grant_access.rb +29 -0
  15. data/app/graphql/masks/server/manage/mutations/group_source.rb +34 -0
  16. data/app/graphql/masks/server/manage/mutations/issue_ldap_credential.rb +5 -2
  17. data/app/graphql/masks/server/manage/mutations/refresh_group.rb +21 -0
  18. data/app/graphql/masks/server/manage/mutations/remove_group_member.rb +2 -0
  19. data/app/graphql/masks/server/manage/mutations/run_health_checks.rb +19 -0
  20. data/app/graphql/masks/server/manage/mutations/unenroll_client.rb +28 -0
  21. data/app/graphql/masks/server/manage/mutations/update_client.rb +6 -0
  22. data/app/graphql/masks/server/manage/mutations/update_group.rb +7 -4
  23. data/app/graphql/masks/server/manage/mutations/update_tenant.rb +14 -1
  24. data/app/graphql/masks/server/manage/types/access_request_type.rb +21 -0
  25. data/app/graphql/masks/server/manage/types/actor_type.rb +6 -0
  26. data/app/graphql/masks/server/manage/types/client_type.rb +18 -0
  27. data/app/graphql/masks/server/manage/types/group_condition_input.rb +16 -0
  28. data/app/graphql/masks/server/manage/types/group_condition_type.rb +29 -0
  29. data/app/graphql/masks/server/manage/types/group_member_type.rb +16 -0
  30. data/app/graphql/masks/server/manage/types/group_type.rb +17 -1
  31. data/app/graphql/masks/server/manage/types/health_check_type.rb +18 -0
  32. data/app/graphql/masks/server/manage/types/ldap_credential_type.rb +1 -0
  33. data/app/graphql/masks/server/manage/types/mutation_type.rb +5 -0
  34. data/app/graphql/masks/server/manage/types/query_type.rb +24 -0
  35. data/app/graphql/masks/server/manage/types/release_type.rb +16 -0
  36. data/app/graphql/masks/server/manage/types/tenant_type.rb +6 -1
  37. data/app/helpers/masks/server/account_helper.rb +4 -0
  38. data/app/jobs/masks/server/application_job.rb +28 -0
  39. data/app/jobs/masks/server/check_domain_claims_job.rb +1 -3
  40. data/app/jobs/masks/server/cleanup_job.rb +5 -6
  41. data/app/jobs/masks/server/health_check_job.rb +21 -0
  42. data/app/jobs/masks/server/idle_accounts_job.rb +1 -3
  43. data/app/jobs/masks/server/prune_queue_job.rb +19 -0
  44. data/app/jobs/masks/server/refresh_groups_job.rb +18 -0
  45. data/app/jobs/masks/server/refresh_saml_metadata_job.rb +1 -3
  46. data/app/jobs/masks/server/release_check_job.rb +21 -0
  47. data/app/jobs/masks/server/rotate_signing_keys_job.rb +1 -3
  48. data/app/jobs/masks/server/text_job.rb +12 -1
  49. data/app/ldap/masks/server/ldap/connection.rb +3 -2
  50. data/app/ldap/masks/server/ldap/gate.rb +31 -4
  51. data/app/logins/masks/server/login_states/second_factor.rb +1 -1
  52. data/app/mailers/masks/server/actor_mailer.rb +31 -0
  53. data/app/models/masks/server/access_request.rb +103 -0
  54. data/app/models/masks/server/actor.rb +6 -0
  55. data/app/models/masks/server/app_password.rb +91 -0
  56. data/app/models/masks/server/app_passwords.rb +82 -0
  57. data/app/models/masks/server/app_preset.rb +102 -0
  58. data/app/models/masks/server/boot_task.rb +41 -0
  59. data/app/models/masks/server/client.rb +15 -0
  60. data/app/models/masks/server/connection.rb +7 -0
  61. data/app/models/masks/server/declared.rb +10 -2
  62. data/app/models/masks/server/email_verification.rb +2 -2
  63. data/app/models/masks/server/enrollment_token.rb +1 -1
  64. data/app/models/masks/server/event.rb +15 -1
  65. data/app/models/masks/server/federation/protocol.rb +1 -1
  66. data/app/models/masks/server/federation/saml.rb +1 -1
  67. data/app/models/masks/server/forward_auth_rules.rb +162 -0
  68. data/app/models/masks/server/group.rb +207 -4
  69. data/app/models/masks/server/group_member.rb +7 -0
  70. data/app/models/masks/server/group_rule.rb +63 -0
  71. data/app/models/masks/server/health.rb +243 -0
  72. data/app/models/masks/server/health_check.rb +27 -0
  73. data/app/models/masks/server/invitation.rb +2 -2
  74. data/app/models/masks/server/job_failures.rb +25 -0
  75. data/app/models/masks/server/ldap_credential.rb +2 -2
  76. data/app/models/masks/server/login.rb +28 -2
  77. data/app/models/masks/server/mail_previews.rb +5 -0
  78. data/app/models/masks/server/mail_template.rb +2 -0
  79. data/app/models/masks/server/membership.rb +10 -3
  80. data/app/models/masks/server/notifications.rb +3 -1
  81. data/app/models/masks/server/password_reset.rb +2 -2
  82. data/app/models/masks/server/provider.rb +1 -0
  83. data/app/models/masks/server/queue.rb +60 -0
  84. data/app/models/masks/server/release.rb +74 -0
  85. data/app/models/masks/server/session.rb +8 -0
  86. data/app/models/masks/server/tenant.rb +39 -2
  87. data/app/models/masks/server/token.rb +2 -1
  88. data/app/views/masks/server/account/_app_passwords.html.erb +83 -0
  89. data/app/views/masks/server/account/_organizations.html.erb +33 -0
  90. data/app/views/masks/server/account/index.html.erb +5 -0
  91. data/app/views/masks/server/actor_mailer/access_declined.html.erb +5 -0
  92. data/app/views/masks/server/actor_mailer/access_declined.text.erb +5 -0
  93. data/app/views/masks/server/actor_mailer/access_granted.html.erb +7 -0
  94. data/app/views/masks/server/actor_mailer/access_granted.text.erb +7 -0
  95. data/app/views/masks/server/authorize/error.html.erb +25 -0
  96. data/config/locales/de/access_requests.yml +43 -0
  97. data/config/locales/de/account/app_passwords.yml +42 -0
  98. data/config/locales/de/events.yml +9 -0
  99. data/config/locales/de/flashes.yml +1 -0
  100. data/config/locales/de/group_sources.yml +5 -0
  101. data/config/locales/de/manage/access_requests.yml +12 -0
  102. data/config/locales/de/manage/app_passwords.yml +16 -0
  103. data/config/locales/de/manage/client.yml +1 -1
  104. data/config/locales/de/manage/forward_auth_rules.yml +29 -0
  105. data/config/locales/de/manage/group_sources.yml +44 -0
  106. data/config/locales/de/manage/health.yml +78 -0
  107. data/config/locales/de/manage/links.yml +20 -0
  108. data/config/locales/de/manage/machines.yml +13 -0
  109. data/config/locales/de/manage/namespaces.yml +1 -1
  110. data/config/locales/en/access_requests.yml +43 -0
  111. data/config/locales/en/account/app_passwords.yml +42 -0
  112. data/config/locales/en/events.yml +9 -0
  113. data/config/locales/en/flashes.yml +1 -0
  114. data/config/locales/en/group_sources.yml +5 -0
  115. data/config/locales/en/manage/access_requests.yml +12 -0
  116. data/config/locales/en/manage/app_passwords.yml +16 -0
  117. data/config/locales/en/manage/forward_auth_rules.yml +29 -0
  118. data/config/locales/en/manage/group_sources.yml +44 -0
  119. data/config/locales/en/manage/health.yml +78 -0
  120. data/config/locales/en/manage/links.yml +20 -0
  121. data/config/locales/en/manage/machines.yml +13 -0
  122. data/config/locales/es/access_requests.yml +43 -0
  123. data/config/locales/es/account/app_passwords.yml +42 -0
  124. data/config/locales/es/events.yml +9 -0
  125. data/config/locales/es/flashes.yml +1 -0
  126. data/config/locales/es/group_sources.yml +5 -0
  127. data/config/locales/es/handshakes.yml +1 -1
  128. data/config/locales/es/manage/access_requests.yml +12 -0
  129. data/config/locales/es/manage/app_passwords.yml +16 -0
  130. data/config/locales/es/manage/forward_auth_rules.yml +29 -0
  131. data/config/locales/es/manage/group_sources.yml +44 -0
  132. data/config/locales/es/manage/health.yml +78 -0
  133. data/config/locales/es/manage/links.yml +20 -0
  134. data/config/locales/es/manage/machines.yml +13 -0
  135. data/config/locales/fr/access_requests.yml +43 -0
  136. data/config/locales/fr/account/app_passwords.yml +42 -0
  137. data/config/locales/fr/events.yml +9 -0
  138. data/config/locales/fr/flashes.yml +1 -0
  139. data/config/locales/fr/group_sources.yml +5 -0
  140. data/config/locales/fr/handshakes.yml +1 -1
  141. data/config/locales/fr/manage/access_requests.yml +12 -0
  142. data/config/locales/fr/manage/app_passwords.yml +16 -0
  143. data/config/locales/fr/manage/client.yml +1 -1
  144. data/config/locales/fr/manage/clients.yml +1 -1
  145. data/config/locales/fr/manage/forward_auth_rules.yml +29 -0
  146. data/config/locales/fr/manage/group_sources.yml +44 -0
  147. data/config/locales/fr/manage/health.yml +78 -0
  148. data/config/locales/fr/manage/links.yml +20 -0
  149. data/config/locales/fr/manage/machines.yml +13 -0
  150. data/config/locales/fr/manage/providers.yml +1 -1
  151. data/config/routes.rb +6 -0
  152. data/db/migrate/20261019140000_add_indexes_for_cleanup_and_metrics.rb +10 -0
  153. data/db/migrate/20261020100000_create_access_requests.rb +28 -0
  154. data/db/migrate/20261020110000_add_app_passwords_to_clients.rb +5 -0
  155. data/db/migrate/20261020120000_add_forward_auth_rules_to_clients.rb +6 -0
  156. data/db/migrate/20261020130000_add_sources_to_groups.rb +15 -0
  157. data/db/migrate/20261020140000_create_health_checks.rb +25 -0
  158. data/db/migrate/20261020150000_add_link_lifetimes_to_tenants.rb +8 -0
  159. data/db/migrate/20261021100000_add_pinned_origin_to_tenants.rb +5 -0
  160. data/lib/generators/masks/server/install/install_generator.rb +3 -2
  161. data/lib/masks/server/configuration.rb +6 -11
  162. data/lib/masks/server/schema_reference.rb +12 -0
  163. data/lib/masks/server/tenancy/middleware.rb +17 -6
  164. data/lib/tasks/masks.rake +31 -7
  165. data/public/masks-assets/.vite/manifest.json +13 -13
  166. data/public/masks-assets/assets/{account-D46qDQCo.js → account-h7I6VUPR.js} +2 -2
  167. data/public/masks-assets/assets/{account-D46qDQCo.js.map → account-h7I6VUPR.js.map} +1 -1
  168. data/public/masks-assets/assets/{app-D7W0gAyT.css → app-BXOScMjL.css} +1 -1
  169. data/public/masks-assets/assets/{handshake-DIFZPSAK.js → handshake-YQpRNMIs.js} +2 -2
  170. data/public/masks-assets/assets/{handshake-DIFZPSAK.js.map → handshake-YQpRNMIs.js.map} +1 -1
  171. data/public/masks-assets/assets/initial-Cn-UuAsy.js +3 -0
  172. data/public/masks-assets/assets/{initial-C1GpCiEh.js.map → initial-Cn-UuAsy.js.map} +1 -1
  173. data/public/masks-assets/assets/{login-C_6igRZ3.js → login-C21_jb6L.js} +3 -3
  174. data/public/masks-assets/assets/{login-C_6igRZ3.js.map → login-C21_jb6L.js.map} +1 -1
  175. data/public/masks-assets/assets/{manage-DR9qQiBU.js → manage-DpVxe5sv.js} +86 -50
  176. data/public/masks-assets/assets/manage-DpVxe5sv.js.map +1 -0
  177. data/public/masks-assets/assets/passkey-5UYPVD3c.js +2 -0
  178. data/public/masks-assets/assets/{passkey-DIj_5LCY.js.map → passkey-5UYPVD3c.js.map} +1 -1
  179. data/version.txt +1 -1
  180. metadata +96 -14
  181. data/public/masks-assets/assets/initial-C1GpCiEh.js +0 -3
  182. data/public/masks-assets/assets/manage-DR9qQiBU.js.map +0 -1
  183. data/public/masks-assets/assets/passkey-DIj_5LCY.js +0 -2
@@ -64,6 +64,12 @@ module Masks
64
64
 
65
65
  IDLE_DAYS = (60..3650)
66
66
  RETENTION_DAYS = (30..2555)
67
+ LINK_LIFETIMES = {
68
+ invitation_lifetime: (1.hour.to_i..30.days.to_i),
69
+ organization_invitation_lifetime: (1.hour.to_i..30.days.to_i),
70
+ password_reset_lifetime: (5.minutes.to_i..1.day.to_i),
71
+ email_verification_lifetime: (1.hour.to_i..14.days.to_i)
72
+ }.freeze
67
73
 
68
74
  validates :named_by, inclusion: { in: NAMES }, allow_nil: true
69
75
  validates :accent_color, format: { with: ACCENT }, allow_nil: true
@@ -71,6 +77,18 @@ module Masks
71
77
  validates :dynamic_registration, inclusion: { in: REGISTRATIONS }, allow_nil: true
72
78
  validates :suspend_after, :delete_after, numericality: { only_integer: true, in: IDLE_DAYS }, allow_nil: true
73
79
  validates :event_retention_days, numericality: { only_integer: true, in: RETENTION_DAYS }, allow_nil: true
80
+ def self.spoken(seconds)
81
+ unit, length = { "day" => 1.day, "hour" => 1.hour, "minute" => 1.minute }.find { |_, held| (seconds % held).zero? }
82
+ count = seconds / length
83
+
84
+ "#{count} #{unit.pluralize(count)}"
85
+ end
86
+
87
+ LINK_LIFETIMES.each do |name, range|
88
+ shortest, longest = [ range.begin, range.end ].map { |seconds| spoken(seconds) }
89
+
90
+ validates name, numericality: { only_integer: true, in: range, message: "must be from #{shortest} to #{longest}" }
91
+ end
74
92
  validates :delete_after, comparison: { greater_than: :suspend_after, message: "must be longer than suspend after" },
75
93
  if: -> { suspend_after && delete_after }
76
94
 
@@ -104,8 +122,20 @@ module Masks
104
122
  event_retention_days&.days || Event::RETENTION
105
123
  end
106
124
 
125
+ def self.link_lifetime(name, tenant = Current.tenant)
126
+ (tenant ? tenant[name] : column_defaults.fetch(name.to_s)).seconds
127
+ end
128
+
129
+ def self.default_link_lifetime(name)
130
+ column_defaults.fetch(name.to_s)
131
+ end
132
+
107
133
  def public_origin
108
- custom_origin || templated_origin
134
+ custom_origin || templated_origin || held_origin
135
+ end
136
+
137
+ def held_origin
138
+ pinned_origin if self.class.pins_origin?
109
139
  end
110
140
 
111
141
  def templated_origin
@@ -246,6 +276,10 @@ module Masks
246
276
  serving(host) || named(host)
247
277
  end
248
278
 
279
+ def pins_origin?
280
+ ::Rails.configuration.masks.public_origin_template.nil? && ::Rails.configuration.masks.pin_origin
281
+ end
282
+
249
283
  def served_domain
250
284
  template = ::Rails.configuration.masks.public_origin_template
251
285
 
@@ -403,7 +437,10 @@ module Masks
403
437
  end
404
438
 
405
439
  def set_up!
406
- update!(setup_token: nil) if setup_token.present?
440
+ self.setup_token = nil
441
+ self.pinned_origin ||= Current.origin.presence if self.class.pins_origin?
442
+
443
+ save! if changed?
407
444
  end
408
445
 
409
446
  def setup_announcement
@@ -22,7 +22,8 @@ module Masks
22
22
  "pushed" => "PushedRequest",
23
23
  "approval" => "SignInApproval",
24
24
  "forward_code" => "ForwardCode",
25
- "forward_session" => "ForwardSession"
25
+ "forward_session" => "ForwardSession",
26
+ "app_password" => "AppPassword"
26
27
  }.freeze
27
28
 
28
29
  self.inheritance_column = "kind"
@@ -0,0 +1,83 @@
1
+ <section class="group" id="app-passwords">
2
+ <h2><%= account_text("app_passwords.heading") %></h2>
3
+
4
+ <div class="entries">
5
+ <details class="entry" <%= "open" if app_password %>>
6
+ <summary>
7
+ <span class="entry-icon"><%= account_icon(:password) %></span>
8
+ <span class="entry-label"><%= account_text("app_passwords.entry") %></span>
9
+ <span class="entry-state">
10
+ <%= app_passwords.any? ? app_passwords.size : account_text("app_passwords.state.none") %>
11
+ </span>
12
+ <span class="entry-mark" aria-hidden="true"></span>
13
+ </summary>
14
+
15
+ <div class="entry-body">
16
+ <p class="aside"><%= account_text("app_passwords.lede") %></p>
17
+
18
+ <% if app_password %>
19
+ <p class="aside aside-bad" role="alert">
20
+ <%= account_text("app_passwords.shown_once", name: app_password["name"], app: app_password["app"]) %>
21
+ </p>
22
+
23
+ <ol class="codes">
24
+ <li><code><%= app_password["secret"] %></code></li>
25
+ </ol>
26
+ <% end %>
27
+
28
+ <% if app_passwords.any? %>
29
+ <div class="items">
30
+ <% app_passwords.each do |held| %>
31
+ <div class="item">
32
+ <span class="item-name">
33
+ <span><%= held.name %></span>
34
+ <span class="tag"><%= held.client.name %></span>
35
+ </span>
36
+
37
+ <span class="item-meta">
38
+ <%= account_text("app_passwords.made_ago", age: time_ago_in_words(held.created_at)) %>
39
+ ·
40
+ <% if held.used_at %>
41
+ <%= account_text("app_passwords.used_ago", age: time_ago_in_words(held.used_at)) %>
42
+ <% else %>
43
+ <%= account_text("app_passwords.never_used") %>
44
+ <% end %>
45
+ </span>
46
+
47
+ <%= button_to account_text("app_passwords.revoke"),
48
+ account_app_password_path(held.id), method: :delete,
49
+ class: "action action-quiet action-sm", form_class: "item-act",
50
+ form: { data: { turbo_confirm: account_text("app_passwords.confirm_revoke", name: held.name) } } %>
51
+ </div>
52
+ <% end %>
53
+ </div>
54
+ <% end %>
55
+
56
+ <% if offered.any? %>
57
+ <%= form_with url: account_app_passwords_path, method: :post, class: "member-add" do |form| %>
58
+ <label class="field member-add-email">
59
+ <span class="field-label"><%= account_text("app_passwords.name") %></span>
60
+ <%= form.text_field :name, required: true, autocomplete: "off", class: "control", id: "app-password-name",
61
+ maxlength: Masks::Server::AppPassword::NAME_LIMIT,
62
+ placeholder: account_text("app_passwords.name_placeholder") %>
63
+ </label>
64
+
65
+ <label class="field member-add-role">
66
+ <span class="field-label"><%= account_text("app_passwords.app") %></span>
67
+ <%= form.select :client_id, offered.map { |client| [ client.name, client.client_id ] }, {}, class: "control",
68
+ id: "app-password-client" %>
69
+ </label>
70
+
71
+ <button type="submit" class="action action-sm member-add-go">
72
+ <%= account_text(fresh ? "app_passwords.make" : "app_passwords.make_again") %>
73
+ </button>
74
+
75
+ <span class="field-hint member-add-hint"><%= account_text("app_passwords.hint") %></span>
76
+ <% end %>
77
+ <% else %>
78
+ <p class="aside"><%= account_text("app_passwords.none_offered") %></p>
79
+ <% end %>
80
+ </div>
81
+ </details>
82
+ </div>
83
+ </section>
@@ -135,6 +135,39 @@
135
135
  <% end %>
136
136
  </div>
137
137
 
138
+ <% asked = account_access_requests(organization) %>
139
+ <% if asked.any? %>
140
+ <p class="aside"><strong><%= account_text("access.heading") %></strong> <%= account_text("access.lede", organization: organization.name) %></p>
141
+
142
+ <div class="items" id="access-requests-<%= organization.key %>">
143
+ <% asked.each do |request| %>
144
+ <% roles = request.organization_roles.filter_map { |key, role| role if key == organization.key } %>
145
+ <div class="item item-member access-request" id="access-request-<%= request.id %>">
146
+ <span class="item-name"><%= request.actor.identifier %></span>
147
+ <span class="item-meta">
148
+ <%= account_text("access.asked", app: request.client.name, age: time_ago_in_words(request.created_at)) %>
149
+ </span>
150
+
151
+ <div class="item-act item-acts">
152
+ <%= form_with url: account_organization_access_path(organization.key, request.id), method: :post,
153
+ class: "item-acts" do |form| %>
154
+ <%= form.select :role, roles, {}, class: "control control-sm",
155
+ id: "access-role-#{organization.key}-#{request.id}",
156
+ aria: { label: account_text("access.role_of", name: request.actor.identifier) } %>
157
+ <button type="submit" class="action action-sm"><%= account_text("access.grant") %></button>
158
+ <% end %>
159
+
160
+ <%= button_to account_text("access.decline"),
161
+ account_organization_access_path(organization.key, request.id), method: :delete,
162
+ class: "action action-quiet action-sm",
163
+ form: { data: { turbo_confirm: account_text("access.confirm_decline", identifier: request.actor.identifier,
164
+ app: request.client.name) } } %>
165
+ </div>
166
+ </div>
167
+ <% end %>
168
+ </div>
169
+ <% end %>
170
+
138
171
  <%= form_with url: account_organization_members_path(organization.key), method: :post, class: "member-add" do |form| %>
139
172
  <label class="field member-add-email">
140
173
  <span class="field-label"><%= account_text("organizations.add_email") %></span>
@@ -31,6 +31,11 @@
31
31
 
32
32
  <%= render "masks/server/account/apps", apps: @apps, connections: @connections, linkable: @linkable %>
33
33
 
34
+ <% if @app_passwords.any? || @app_password_clients.any? %>
35
+ <%= render "masks/server/account/app_passwords", app_passwords: @app_passwords, offered: @app_password_clients,
36
+ app_password: @app_password, fresh: @fresh %>
37
+ <% end %>
38
+
34
39
  <%= render "masks/server/account/info", actor: @actor, directed: @directed, fresh: @fresh %>
35
40
 
36
41
  <%= render "masks/server/account/activity", events: @events %>
@@ -0,0 +1,5 @@
1
+ <%= render layout: "message" do %>
2
+ <p class="lede"><%= t(".lede", app: @app.name, tenant: @tenant_name) %></p>
3
+ <% end %>
4
+
5
+ <p class="meta"><%= t(".again") %></p>
@@ -0,0 +1,5 @@
1
+ <%= render layout: "message" do %>
2
+ <%= t(".lede", app: @app.name, tenant: @tenant_name) %>
3
+ <% end %>
4
+
5
+ <%= t(".again") %>
@@ -0,0 +1,7 @@
1
+ <%= render layout: "message" do %>
2
+ <p class="lede"><%= t(".lede", app: @app.name, tenant: @tenant_name) %></p>
3
+ <% end %>
4
+
5
+ <% if @url %>
6
+ <%= render "action", url: @url, label: t(".action", app: @app.name) %>
7
+ <% end %>
@@ -0,0 +1,7 @@
1
+ <%= render layout: "message" do %>
2
+ <%= t(".lede", app: @app.name, tenant: @tenant_name) %>
3
+ <% end %>
4
+ <% if @url %>
5
+
6
+ <%= @url %>
7
+ <% end %>
@@ -9,4 +9,29 @@
9
9
  <p class="said"><%= @error_code %> — <%= @error_description %></p>
10
10
 
11
11
  <p class="prompt-lede"><%= @refused ? t(".nothing_shared") : t(".contact") %></p>
12
+
13
+ <% if @asking %>
14
+ <div id="ask-access" class="flow" data-waiting="<%= @waiting.present? %>">
15
+ <% if flash[:alert].present? %>
16
+ <p class="note note-bad" role="alert"><%= flash[:alert] %></p>
17
+ <% elsif flash[:notice].present? %>
18
+ <p class="note note-plain" role="status"><%= flash[:notice] %></p>
19
+ <% end %>
20
+
21
+ <% if @waiting %>
22
+ <p class="prompt-lede"><%= t(".waiting", app: @asking.name) %></p>
23
+ <% else %>
24
+ <p class="prompt-lede"><%= t(".asking", app: @asking.name) %></p>
25
+
26
+ <%= form_with url: authorize_access_request_path, method: :post do |form| %>
27
+ <%= form.hidden_field :rid, value: params[:rid] %>
28
+ <button type="submit" class="action"><%= t(".ask") %></button>
29
+ <% end %>
30
+ <% end %>
31
+ </div>
32
+ <% end %>
33
+
34
+ <% if @return_to %>
35
+ <a class="action action-quiet" id="back-to-app" href="<%= @return_to %>"><%= t(".back", app: @asking&.name || t(".the_app")) %></a>
36
+ <% end %>
12
37
  </div>
@@ -0,0 +1,43 @@
1
+ de:
2
+ authorize:
3
+ error:
4
+ asking: "Ihr Konto ist in keiner Gruppe, die %{app} nutzen darf. Sie können um Zugang bitten. Die Betreiber von %{app} entscheiden, und Sie erhalten in jedem Fall eine E-Mail."
5
+ ask: "Um Zugang bitten"
6
+ asked: "Ihre Anfrage wurde gesendet. Sie erhalten eine E-Mail, sobald sie entschieden ist."
7
+ waiting: "Ihre Anfrage, %{app} zu nutzen, wartet. Sie erhalten eine E-Mail, sobald sie entschieden ist."
8
+ slow_down: "Sie haben vor weniger als einer Stunde um Zugang zu dieser App gebeten. Warten Sie auf die Antwort."
9
+ back: "Zurück zu %{app}"
10
+ the_app: "der App"
11
+
12
+ account:
13
+ index:
14
+ access:
15
+ heading: "Um Zugang gebeten"
16
+ lede: "Diese Personen wurden von einer App abgewiesen, die Mitglieder von %{organization} zulässt. Mit Genehmigen werden sie Mitglied mit der Rolle, die Sie wählen."
17
+ asked: "Hat vor %{age} gebeten, %{app} zu nutzen"
18
+ role_of: "Rolle für %{name}"
19
+ grant: "Genehmigen"
20
+ decline: "Ablehnen"
21
+ confirm_decline: "Die Anfrage von %{identifier}, %{app} zu nutzen, ablehnen? Die Person erhält eine E-Mail und kann in einer Stunde erneut bitten."
22
+
23
+ organization_access:
24
+ granted: "%{identifier} ist jetzt %{role} von %{organization} und kann %{app} nutzen."
25
+ declined: "Die Anfrage von %{identifier}, %{app} zu nutzen, ist abgelehnt."
26
+ unknown: "Diese Anfrage wartet nicht auf %{organization}."
27
+
28
+ actor_mailer:
29
+ access_granted:
30
+ subject: "Sie können %{app} jetzt nutzen"
31
+ lede: "Ihre Anfrage, %{app} bei %{tenant} zu nutzen, wurde genehmigt. Melden Sie sich erneut bei %{app} an, und Sie werden eingelassen."
32
+ action: "%{app} öffnen"
33
+ access_declined:
34
+ subject: "Ihre Anfrage, %{app} zu nutzen, wurde abgelehnt"
35
+ lede: "Jemand, der %{app} bei %{tenant} betreibt, hat Ihre Anfrage um Zugang abgelehnt."
36
+ again: "Wenn Sie das für einen Fehler halten, fragen Sie die Person direkt. In einer Stunde können Sie erneut bitten."
37
+
38
+ events:
39
+ actions:
40
+ access:
41
+ requested: "Um Zugang zu einer App gebeten"
42
+ granted: "Zugang zu einer App genehmigt"
43
+ declined: "Zugang zu einer App abgelehnt"
@@ -0,0 +1,42 @@
1
+ de:
2
+ account:
3
+ index:
4
+ app_passwords:
5
+ heading: "App-Passwörter"
6
+ entry: "App-Passwörter"
7
+ state:
8
+ none: "Keine"
9
+ lede: "Mit einem App-Passwort meldet sich eine einzelne App, etwa ein Medienserver oder ein NAS, ohne Ihren zweiten Faktor als Sie an. Es gilt nur für die App, für die es erstellt wurde."
10
+ shown_once: "Das ist das App-Passwort für %{name}. Es wird nur einmal angezeigt. Geben Sie es jetzt in %{app} ein."
11
+ made_ago: "erstellt vor %{age}"
12
+ used_ago: "zuletzt verwendet vor %{age}"
13
+ never_used: "nie verwendet"
14
+ revoke: "Widerrufen"
15
+ confirm_revoke: "%{name} widerrufen? Die App kann sich damit sofort nicht mehr anmelden."
16
+ name: "Name"
17
+ name_placeholder: "Jellyfin auf dem Fernseher"
18
+ app: "App"
19
+ make: "App-Passwort erstellen"
20
+ make_again: "Zum Erstellen erneut anmelden"
21
+ hint: "Ihr eigenes Passwort und Ihre Passkeys bleiben unverändert. Das Widerrufen eines App-Passworts ändert sonst nichts."
22
+ none_offered: "Noch keine App akzeptiert App-Passwörter."
23
+ app_passwords:
24
+ again: "Melden Sie sich erneut an, um ein App-Passwort zu erstellen. Ihre letzte Anmeldung liegt eine Weile zurück."
25
+ unknown: "Dieses Konto hat kein solches App-Passwort."
26
+ revoked: "%{name} ist widerrufen."
27
+ refused:
28
+ name: "Geben Sie dem App-Passwort einen Namen mit höchstens 64 Zeichen."
29
+ app: "Diese App akzeptiert keine App-Passwörter."
30
+ too_many: "Sie haben bereits %{most} App-Passwörter. Widerrufen Sie zuerst eines."
31
+ events:
32
+ actions:
33
+ app_password:
34
+ created: "App-Passwort erstellt"
35
+ used: "Mit einem App-Passwort angemeldet"
36
+ revoked: "App-Passwort widerrufen"
37
+ actor_mailer:
38
+ notification:
39
+ said:
40
+ app_password:
41
+ created: "Für Ihr %{tenant}-Konto wurde ein App-Passwort erstellt. Damit meldet sich eine App ohne Ihren zweiten Faktor an."
42
+ revoked: "Ein App-Passwort Ihres %{tenant}-Kontos wurde widerrufen."
@@ -157,6 +157,13 @@ de:
157
157
  exported: "Aktivität exportiert"
158
158
  mail_template:
159
159
  updated: "E-Mail-Vorlage geändert"
160
+ job:
161
+ failed: "Hintergrundjob für diesen Mandanten fehlgeschlagen"
162
+ health:
163
+ failing: "Zustandsprüfung fehlgeschlagen"
164
+ recovered: "Zustandsprüfung wieder in Ordnung"
165
+ release:
166
+ available: "Neue Version verfügbar"
160
167
  stream:
161
168
  created: "Event-Stream hinzugefügt"
162
169
  updated: "Event-Stream geändert"
@@ -171,6 +178,8 @@ de:
171
178
  signal:
172
179
  undelivered: "Shared Signal nicht zugestellt"
173
180
  received: "Anbieter hat Abmeldung signalisiert"
181
+ text:
182
+ undelivered: "SMS nicht zugestellt"
174
183
  organization:
175
184
  created: "Organisation hinzugefügt"
176
185
  updated: "Organisation geändert"
@@ -163,3 +163,4 @@ de:
163
163
  forward_auth:
164
164
  unknown: "Unter dieser Adresse ist keine App hinter einem Proxy registriert."
165
165
  not_admitted: "Ihr Konto darf %{app} nicht verwenden."
166
+ denied: "Diese Seite von %{app} ist für alle gesperrt."
@@ -0,0 +1,5 @@
1
+ de:
2
+ events:
3
+ actions:
4
+ group:
5
+ refreshed: "Gruppe aus ihrer Quelle gefüllt"
@@ -0,0 +1,12 @@
1
+ de:
2
+ manage:
3
+ access_requests:
4
+ title: "Zugangsanfragen"
5
+ lede: "Akteure, die von diesem Client abgewiesen wurden und um Zugang gebeten haben. Mit Genehmigen kommen sie in eine der Gruppen, die er zulässt, und sie erhalten in jedem Fall eine E-Mail."
6
+ empty: "Niemand wartet auf Zugang."
7
+ asked: "Gebeten %{day}"
8
+ as: "Gruppe, in die sie kommen"
9
+ grant: "Genehmigen"
10
+ decline: "Ablehnen"
11
+ granted: "%{identifier} ist jetzt in %{label} und kann sich anmelden."
12
+ declined: "Die Anfrage von %{identifier} ist abgelehnt."
@@ -0,0 +1,16 @@
1
+ de:
2
+ manage:
3
+ client:
4
+ sign_in:
5
+ app_passwords: "App-Passwörter akzeptieren"
6
+ app_passwords_help: "Akteure erstellen auf ihrer Kontoseite ein App-Passwort für diesen Client. Es meldet sie über LDAP und Forward-Auth ohne zweiten Faktor an, nur bei diesem Client."
7
+ notices:
8
+ app_passwords_on: "Akteure können jetzt ein App-Passwort dafür erstellen."
9
+ app_passwords_off: "Seine App-Passwörter werden ab jetzt abgelehnt."
10
+ ldap:
11
+ connect:
12
+ app: "App"
13
+ no_app: "Keine App"
14
+ app_help: "Wählen Sie den Client, als der der Dienst registriert ist. App-Passwörter der Akteure für diesen Client binden dann auf einer Verbindung, die sich zuerst mit diesem Zugang gebunden hat."
15
+ credentials:
16
+ app: "Akzeptiert App-Passwörter für %{name}"
@@ -174,7 +174,7 @@ de:
174
174
  uris:
175
175
  title: "URIs"
176
176
  acs: "Assertion Consumer Services"
177
- redirect: "Redirect"
177
+ redirect: "Weiterleitung"
178
178
  post_logout: "Redirect nach dem Logout"
179
179
  resources: "Ressourcen"
180
180
  details:
@@ -0,0 +1,29 @@
1
+ de:
2
+ manage:
3
+ forward_auth_rules:
4
+ title: "Regeln"
5
+ lede: "Die erste Regel, die eine Anfrage abdeckt, entscheidet sie. Eine Anfrage ohne passende Regel braucht eine angemeldete Person, die sich anmelden darf."
6
+ empty: "Es gibt keine Regeln. Jede Anfrage braucht eine angemeldete Person, die sich anmelden darf."
7
+ paths: "Pfade"
8
+ paths_hint: "Einer pro Zeile. * deckt ein Segment ab und ** jede Tiefe, also deckt /admin/** /admin und alles darunter ab. Leer deckt jeden Pfad ab."
9
+ methods: "Methoden"
10
+ methods_hint: "GET, POST und so weiter. Leer deckt jede Methode ab."
11
+ networks: "Netzwerke"
12
+ networks_hint: "Adressen oder Bereiche wie 192.168.0.0/16. Leer deckt jede Adresse ab."
13
+ groups: "Gruppen"
14
+ groups_hint: "Gruppenschlüssel oder Rollen als schlüssel:rolle. Leer deckt alle ab."
15
+ outcome: "Ergebnis"
16
+ outcomes:
17
+ open: "Offen für alle, ohne Anmeldung"
18
+ one_factor: "Eine angemeldete Person, ohne zweiten Faktor"
19
+ two_factor: "Eine angemeldete Person, mit ihrem zweiten Faktor"
20
+ token: "Eine angemeldete Person oder ein Skript mit einem Zugriffstoken"
21
+ deny: "Für alle gesperrt"
22
+ rule: "Regel %{number}"
23
+ add: "Regel hinzufügen"
24
+ remove: "Entfernen"
25
+ up: "Nach oben"
26
+ down: "Nach unten"
27
+ save: "Regeln speichern"
28
+ saved: "Regeln gespeichert."
29
+ trust: "Eine Netzwerkregel vertraut der Adresse, die der Proxy sendet. Stellen Sie sicher, dass der Proxy X-Forwarded-For selbst setzt."
@@ -0,0 +1,44 @@
1
+ de:
2
+ manage:
3
+ group_sources:
4
+ source: "Füllt sich aus"
5
+ sources:
6
+ manual: "Von Hand hinzugefügte Personen"
7
+ rule: "Einer Regel"
8
+ provider: "Einem Anbieter"
9
+ group: "Anderen Gruppen"
10
+ ledes:
11
+ manual: "Ein Verwalter fügt Personen auf deren eigenen Seiten hinzu und entfernt sie dort."
12
+ rule: "Alle, die jede Bedingung erfüllen. Eine E-Mail-Adresse zählt, sobald sie bestätigt ist. Ein Profilfeld zählt nur, wenn ein Verzeichnis es setzt, denn solche Felder können Personen nicht ändern. Geprüft bei jeder Profiländerung und jede Stunde."
13
+ provider: "Alle, deren letzte Anmeldung über den Anbieter die Gruppe aufgeführt hat."
14
+ group: "Alle in einer der übergeordneten Gruppen oder Organisationsrollen."
15
+ change: "Ändern, wie sich %{name} füllt"
16
+ kinds:
17
+ email_domain: "E-Mail-Adresse endet auf"
18
+ profile: "Profilfeld ist gleich"
19
+ field: "Feld"
20
+ value: "Wert"
21
+ add_condition: "Bedingung hinzufügen"
22
+ remove_condition: "Bedingung entfernen"
23
+ provider: "Anbieter"
24
+ choose_provider: "Anbieter wählen"
25
+ no_providers: "Fügen Sie zuerst einen Anbieter hinzu."
26
+ upstream_group: "Gruppenname beim Anbieter"
27
+ parent_labels: "Übergeordnete Gruppen"
28
+ parent_labels_hint: "Gruppenschlüssel oder Organisationsrollen als schlüssel:rolle, durch Kommas getrennt."
29
+ save: "Speichern"
30
+ cancel: "Abbrechen"
31
+ saved: "%{name} füllt sich jetzt aus %{source}."
32
+ refresh: "Jetzt auffrischen"
33
+ refreshed: "%{name} ist aufgefrischt."
34
+ refreshed_at: "Aufgefrischt %{when}."
35
+ never_refreshed: "Noch nicht aufgefrischt."
36
+ summary:
37
+ rule: "Füllt sich per Regel"
38
+ provider: "Füllt sich aus %{provider}"
39
+ group: "Füllt sich aus %{parents}"
40
+ by_hand: "Von Hand hinzugefügt, daher entfernt nur ein Verwalter die Person."
41
+ from:
42
+ rule: "Durch die Regel in der Gruppe, daher entscheidet die Regel."
43
+ provider: "Durch eine Anmeldung beim Anbieter in der Gruppe, daher entscheidet der Anbieter."
44
+ group: "Über eine übergeordnete Gruppe in der Gruppe, daher entscheidet diese."
@@ -0,0 +1,78 @@
1
+ de:
2
+ manage:
3
+ health:
4
+ title: "Zustand"
5
+ lede: "Die Prüfungen laufen alle 10 Minuten. Jede sagt, was sie gefunden hat und was zu tun ist."
6
+ check_now: "Jetzt prüfen"
7
+ checking: "Prüft"
8
+ checked: "Geprüft %{when}"
9
+ never: "Die Prüfungen sind noch nicht gelaufen. Sie laufen alle 10 Minuten, sobald ein Worker läuft."
10
+ failed: "Die Prüfungen konnten nicht laufen: %{failure}"
11
+ statuses:
12
+ ok: "In Ordnung"
13
+ warning: "Braucht einen Blick"
14
+ failing: "Fehlgeschlagen"
15
+ checks:
16
+ jobs:
17
+ title: "Hintergrundjobs"
18
+ elsewhere: "Jobs laufen auf einem anderen Server."
19
+ none: "Am letzten Tag ist kein Job fehlgeschlagen."
20
+ failed:
21
+ one: "Eine Art Job ist am letzten Tag fehlgeschlagen: %{jobs}."
22
+ other: "%{count} Arten von Jobs sind am letzten Tag fehlgeschlagen: %{jobs}."
23
+ advice: "Lesen Sie den Fehler im Serverprotokoll. Jeder Job läuft nach seinem Zeitplan erneut."
24
+ worker:
25
+ title: "Worker"
26
+ elsewhere: "Jobs laufen auf einem anderen Server."
27
+ unchecked: "Die Worker-Prüfung ist aus."
28
+ reporting: "Ein Worker läuft."
29
+ silent: "In den letzten %{minutes} Minuten hat sich kein Worker gemeldet."
30
+ advice: "Starten Sie den Jobs-Prozess, oder setzen Sie MASKS_JOBS_IN_WEB_SERVER auf true, damit der Webserver die Jobs ausführt."
31
+ queue:
32
+ title: "Warteschlange"
33
+ elsewhere: "Jobs laufen auf einem anderen Server."
34
+ unchecked: "Die Warteschlangen-Prüfung ist aus."
35
+ moving: "Jobs starten pünktlich."
36
+ stuck: "Ein Job wartet seit %{since} auf seinen Start, länger als %{minutes} Minuten."
37
+ advice: "Fügen Sie einen Worker hinzu, oder erhöhen Sie JOB_CONCURRENCY, damit mehr Jobs gleichzeitig laufen."
38
+ ldap:
39
+ title: "LDAP"
40
+ tls: "LDAP antwortet mit TLS."
41
+ local: "LDAP antwortet nur auf der eigenen Maschine des Servers."
42
+ plain: "LDAP antwortet auf Port %{port} ohne TLS, also gehen Passwörter im Klartext durchs Netz."
43
+ advice: "Setzen Sie MASKS_LDAP_CERT und MASKS_LDAP_KEY, oder entfernen Sie MASKS_LDAP_PLAINTEXT."
44
+ metrics:
45
+ title: "Metriken"
46
+ "off": "Die Metrikseite ist aus."
47
+ "on": "Die Metrikseite beantwortet Anfragen, die ihr Token tragen."
48
+ short: "Das Metrik-Token ist %{length} Zeichen lang und lässt sich erraten."
49
+ advice: "Setzen Sie MASKS_METRICS_TOKEN auf mindestens %{needed} zufällige Zeichen."
50
+ mail:
51
+ title: "E-Mail"
52
+ none: "Kein E-Mail-Dienst ist eingerichtet."
53
+ answers: "Der Mailserver antwortet."
54
+ silent: "Der Mailserver hat nicht geantwortet: %{error}."
55
+ advice: "Prüfen Sie Server, Port und TLS-Einstellung auf der Seite Nachrichten, und ob der Server Verbindungen von hier zulässt."
56
+ origin:
57
+ title: "Öffentliche Adresse"
58
+ unset: "Keine öffentliche Adresse ist gesetzt, also folgt masks dem Host-Header jeder Anfrage."
59
+ matches: "Anfragen kommen an der öffentlichen Adresse dieses Mandanten an."
60
+ mismatch: "Anfragen kommen als %{seen} an, aber die Adresse dieses Mandanten ist %{origin}."
61
+ advice: "Lassen Sie den Proxy X-Forwarded-Proto und X-Forwarded-Host senden, und tragen Sie ihn in MASKS_TRUSTED_PROXIES ein."
62
+ domains:
63
+ title: "Domains"
64
+ none: "Keine Domain ist beansprucht."
65
+ proven: "Jede beanspruchte Domain hat noch ihren TXT-Eintrag."
66
+ missing: "Der TXT-Eintrag fehlt für %{domains}. Ein Anspruch wird eine Woche nach dem Verschwinden seines Eintrags freigegeben."
67
+ advice: "Legen Sie den TXT-Eintrag wieder unter dem Namen an, der auf der Seite Domains steht."
68
+ keys:
69
+ title: "Signaturschlüssel"
70
+ missing: "Dieser Mandant hat keinen aktiven Signaturschlüssel, also kann nichts signiert werden."
71
+ manual: "Schlüssel werden von Hand rotiert. Der aktive Schlüssel ist %{days} Tage alt."
72
+ fresh: "Der aktive Schlüssel ist %{days} Tage alt und rotiert alle %{period} Tage."
73
+ stale: "Der aktive Schlüssel ist %{days} Tage alt, älter als seine Rotation von %{period} Tagen."
74
+ advice: "Rotieren Sie den Schlüssel auf der Seite Einstellungen, oder lesen Sie den Job signing_keys im Serverprotokoll."
75
+ release:
76
+ available: "Version %{version} ist verfügbar."
77
+ operator: "Diese Version braucht einen Schritt von einem Betreiber. Lesen Sie ihre Hinweise vor dem Upgrade."
78
+ notes: "Versionshinweise"
@@ -0,0 +1,20 @@
1
+ de:
2
+ manage:
3
+ links:
4
+ title: "Links"
5
+ help: "Wie lange jeder Link gültig ist, den masks per E-Mail sendet. Ein bereits gesendeter Link behält die Gültigkeit, mit der er gesendet wurde. Eine Einladung in eine Organisation zählt ab dem letzten Senden."
6
+ invitation: "Einladung zu einem Konto"
7
+ organization_invitation: "Einladung in eine Organisation"
8
+ password_reset: "Passwort zurücksetzen"
9
+ email_verification: "E-Mail-Bestätigung"
10
+ default: "%{length} (Standard)"
11
+ updated: "Gültigkeit des Links gespeichert."
12
+ minutes:
13
+ one: "%{count} Minute"
14
+ other: "%{count} Minuten"
15
+ hours:
16
+ one: "%{count} Stunde"
17
+ other: "%{count} Stunden"
18
+ days:
19
+ one: "%{count} Tag"
20
+ other: "%{count} Tage"
@@ -0,0 +1,13 @@
1
+ de:
2
+ manage:
3
+ machines:
4
+ title: "Maschinen"
5
+ lede: "Clients, die diese App mit einem Registrierungstoken angemeldet hat. Jeder meldet sich mit einem eigenen Schlüssel an."
6
+ empty: "Diese App hat noch keine Maschine angemeldet."
7
+ last_seen: "Zuletzt angemeldet %{day}"
8
+ never: "Nie angemeldet"
9
+ enrolled: "Angemeldet %{day}"
10
+ enrolled_by: "Eine Maschine, angemeldet von"
11
+ revoke: "Widerrufen"
12
+ revoke_confirm: "%{name} widerrufen? Sie wird archiviert, jedes Token endet, und sie kann sich nicht mehr anmelden."
13
+ revoked: "%{name} ist widerrufen und kann sich nicht mehr anmelden."