summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/controllers/users_controller.rb30
-rw-r--r--app/helpers/node_actions_helper.rb14
-rw-r--r--app/models/user.rb31
-rw-r--r--app/views/users/_user.html.erb17
-rw-r--r--app/views/users/index.html.erb1
5 files changed, 88 insertions, 5 deletions
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 583ebac0..9b9d64e5 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -5,17 +5,17 @@ class UsersController < ApplicationController
5 # Private 5 # Private
6 6
7 before_action :login_required 7 before_action :login_required
8 before_action :find_user, :only => [:show, :edit, :update, :reset_otp, :deactivate, :reactivate] 8 before_action :find_user, :only => [:show, :edit, :update, :reset_otp, :deactivate, :reactivate, :grant_redaktion, :revoke_redaktion]
9 before_action :require_admin, :only => [:index, :new, :create, :reset_otp, :deactivate, :reactivate] 9 before_action :require_redaktion, :only => [:index]
10 before_action :require_admin, :only => [:new, :create, :reset_otp, :deactivate, :reactivate]
10 before_action :require_elevation, :only => [:new, :create, :reset_otp, :deactivate, :reactivate] 11 before_action :require_elevation, :only => [:new, :create, :reset_otp, :deactivate, :reactivate]
11 before_action :verify_status, :except => [:index] 12 before_action :verify_status, :except => [:index, :grant_redaktion, :revoke_redaktion]
12 13
13 layout 'admin' 14 layout 'admin'
14 15
15 ROLE_PRESETS = { 16 ROLE_PRESETS = {
16 "editor" => [], 17 "editor" => [],
17 "redaktion" => ["redaktion"], 18 "redaktion" => ["redaktion"],
18 "admin" => ["admin", "redaktion"]
19 }.freeze 19 }.freeze
20 20
21 GROUP_ORDER = [:admin, :redaktion, :editor, :alumni].freeze 21 GROUP_ORDER = [:admin, :redaktion, :editor, :alumni].freeze
@@ -74,6 +74,28 @@ class UsersController < ApplicationController
74 redirect_to users_path 74 redirect_to users_path
75 end 75 end
76 76
77 def grant_redaktion
78 return deny_role_access(:redaktion_required) unless current_user.redaktion?
79
80 case @user.grant_redaktion!(:actor => current_user)
81 when :granted then flash[:notice] = t("flash.users.redaktion_granted", :login => @user.login)
82 when :no_second_factor then flash[:error] = t("flash.users.redaktion_needs_otp", :login => @user.login)
83 end
84
85 redirect_to users_path
86 end
87
88 def revoke_redaktion
89 return deny_role_access(:redaktion_required) unless current_user.redaktion?
90
91 case @user.revoke_redaktion!(:actor => current_user)
92 when :revoked then flash[:notice] = t("flash.users.redaktion_revoked", :login => @user.login)
93 when :self then flash[:error] = t("flash.users.redaktion_not_self")
94 end
95
96 redirect_to users_path
97 end
98
77 def reset_otp 99 def reset_otp
78 @user.disable_otp!(:actor => current_user) 100 @user.disable_otp!(:actor => current_user)
79 flash[:notice] = t("flash.users.otp_reset", :login => @user.login) 101 flash[:notice] = t("flash.users.otp_reset", :login => @user.login)
diff --git a/app/helpers/node_actions_helper.rb b/app/helpers/node_actions_helper.rb
index f57ef84f..53f6ecd0 100644
--- a/app/helpers/node_actions_helper.rb
+++ b/app/helpers/node_actions_helper.rb
@@ -20,7 +20,9 @@ module NodeActionsHelper
20 "otp_disable" => "shield-off", 20 "otp_disable" => "shield-off",
21 "otp_reset" => "shield-x", 21 "otp_reset" => "shield-x",
22 "user_deactivate" => "user-off", 22 "user_deactivate" => "user-off",
23 "user_reactivate" => "user-check" 23 "user_reactivate" => "user-check",
24 "redaktion_grant" => "user-plus",
25 "redaktion_revoke" => "user-minus"
24 }.freeze 26 }.freeze
25 27
26 def verb_icon action 28 def verb_icon action
@@ -283,4 +285,14 @@ module NodeActionsHelper
283 t("node_actions.user_reactivate", :actor => actor_ref(action), 285 t("node_actions.user_reactivate", :actor => actor_ref(action),
284 :target => user_participant_ref(action)).html_safe 286 :target => user_participant_ref(action)).html_safe
285 end 287 end
288
289 def summarize_redaktion_grant action
290 t("node_actions.redaktion_grant", :actor => actor_ref(action),
291 :target => user_participant_ref(action)).html_safe
292 end
293
294 def summarize_redaktion_revoke action
295 t("node_actions.redaktion_revoke", :actor => actor_ref(action),
296 :target => user_participant_ref(action)).html_safe
297 end
286end 298end
diff --git a/app/models/user.rb b/app/models/user.rb
index bf0f40ee..c3035a02 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -25,6 +25,7 @@ class User < ApplicationRecord
25 :message => Authentication.bad_email_message 25 :message => Authentication.bad_email_message
26 26
27 validate :roles_are_known 27 validate :roles_are_known
28 validate :admin_needs_second_factor
28 29
29 # Authenticates a user by their login name and unencrypted password. Returns the user or nil. 30 # Authenticates a user by their login name and unencrypted password. Returns the user or nil.
30 def self.authenticate(login, password) 31 def self.authenticate(login, password)
@@ -136,6 +137,30 @@ class User < ApplicationRecord
136 true 137 true
137 end 138 end
138 139
140 def grant_redaktion!(actor:)
141 return :already if redaktion?
142 return :no_second_factor unless otp_enrolled?
143
144 transaction do
145 update_column(:roles, (roles | ["redaktion"]).sort)
146 NodeAction.record!(:participants => [self], :user => actor,
147 :action => "redaktion_grant", :target_login => login)
148 end
149 :granted
150 end
151
152 def revoke_redaktion!(actor:)
153 return :already unless redaktion?
154 return :self unless actor != self
155
156 transaction do
157 update_column(:roles, (roles - ["redaktion"]).sort)
158 NodeAction.record!(:participants => [self], :user => actor,
159 :action => "redaktion_revoke", :target_login => login)
160 end
161 :revoked
162 end
163
139 # otp_secret present == enrolled. otp_pending_secret holds the secret 164 # otp_secret present == enrolled. otp_pending_secret holds the secret
140 # between QR display and first-code confirmation. otp_consumed_timestep 165 # between QR display and first-code confirmation. otp_consumed_timestep
141 # makes every accepted code single-use (replay guard within the drift 166 # makes every accepted code single-use (replay guard within the drift
@@ -213,4 +238,10 @@ class User < ApplicationRecord
213 unknown = roles.to_a - ROLES 238 unknown = roles.to_a - ROLES
214 errors.add(:roles, :unknown, :list => unknown.join(", ")) if unknown.any? 239 errors.add(:roles, :unknown, :list => unknown.join(", ")) if unknown.any?
215 end 240 end
241
242 def admin_needs_second_factor
243 return unless roles.include?("admin")
244 return if otp_secret.present?
245 errors.add(:roles, :admin_needs_otp)
246 end
216end 247end
diff --git a/app/views/users/_user.html.erb b/app/views/users/_user.html.erb
index ff9d4e37..ba82375d 100644
--- a/app/views/users/_user.html.erb
+++ b/app/views/users/_user.html.erb
@@ -26,5 +26,22 @@
26 <% end %> 26 <% end %>
27 <% end %> 27 <% end %>
28 </td> 28 </td>
29 <td>
30 <% if current_user.redaktion? && !user.alumni? %>
31 <% if user.redaktion? %>
32 <% unless user == current_user %>
33 <%= button_to t(".revoke_redaktion"), revoke_redaktion_user_path(user), method: :put,
34 form: { data: { confirm: t(".confirm_revoke_redaktion", :login => user.login) },
35 class: 'button_to destructive' } %>
36 <% end %>
37 <% elsif user.otp_enrolled? %>
38 <%= button_to t(".grant_redaktion"), grant_redaktion_user_path(user), method: :put,
39 form: { data: { confirm: t(".confirm_grant_redaktion", :login => user.login) },
40 class: 'button_to state_changing' } %>
41 <% else %>
42 <span class="field_hint"><%= t(".needs_otp") %></span>
43 <% end %>
44 <% end %>
45 </td>
29</tr> 46</tr>
30<% end %> 47<% end %>
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
index 854811a2..2936bbea 100644
--- a/app/views/users/index.html.erb
+++ b/app/views/users/index.html.erb
@@ -8,6 +8,7 @@
8 <% end %> 8 <% end %>
9 <% end %> 9 <% end %>
10</p> 10</p>
11<p class="field_hint"><%= t(".admin_hint") %></p>
11 12
12<% UsersController::GROUP_ORDER.each do |group| %> 13<% UsersController::GROUP_ORDER.each do |group| %>
13 <% members = @users[group] || [] %> 14 <% members = @users[group] || [] %>