summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorerdgeist <erdgeist@erdgeist.org>2026-08-01 00:27:34 +0200
committererdgeist <erdgeist@erdgeist.org>2026-08-01 00:27:34 +0200
commit8bcacace28df52fd972c54e6850aa3b93f5c8bdf (patch)
tree05e90dd4e8f31ebb142f66239025da15e873901f /app
parent529f81b28ed77c62acaa63fad957e751798f2440 (diff)
Declare role requirements per controller
RoleRequired supplies require_redaktion and require_admin for surfaces that are not nodes and so cannot be reached by Node#restricted?. Navigation is content rather than plumbing, so menu_items requires redaktion. User management is janitorial and requires admin: index, new, create, reset_otp, deactivate, reactivate. verify_status now also covers show, without which any logged-in user could read any account by walking a small id space. Editing your own account stays open. The dashboard hides the Users and Navigation buttons from those who cannot use them; everything else stays visible to everyone. Both denials share one message and land on the dashboard. Adds redella (redaktion) and alufa (redaktion + alumni) fixtures.
Diffstat (limited to 'app')
-rw-r--r--app/controllers/concerns/role_required.rb23
-rw-r--r--app/controllers/menu_items_controller.rb2
-rw-r--r--app/controllers/users_controller.rb12
-rw-r--r--app/views/admin/index.html.erb12
4 files changed, 37 insertions, 12 deletions
diff --git a/app/controllers/concerns/role_required.rb b/app/controllers/concerns/role_required.rb
new file mode 100644
index 00000000..b841b8cc
--- /dev/null
+++ b/app/controllers/concerns/role_required.rb
@@ -0,0 +1,23 @@
1# Controller-level role gates, for surfaces that are not nodes and so cannot
2# be reached by Node#restricted?. The node gates live in the models, since
3# those verbs are callable from rake tasks; these are HTTP-only.
4module RoleRequired
5 extend ActiveSupport::Concern
6
7 private
8
9 def require_redaktion
10 return if current_user&.redaktion?
11 deny_role_access(:redaktion_required)
12 end
13
14 def require_admin
15 return if current_user&.is_admin?
16 deny_role_access(:admin_required)
17 end
18
19 def deny_role_access(key)
20 flash[:error] = t("flash.common.#{key}")
21 redirect_to admin_path
22 end
23end
diff --git a/app/controllers/menu_items_controller.rb b/app/controllers/menu_items_controller.rb
index f169e0ca..63935f13 100644
--- a/app/controllers/menu_items_controller.rb
+++ b/app/controllers/menu_items_controller.rb
@@ -1,9 +1,11 @@
1class MenuItemsController < ApplicationController 1class MenuItemsController < ApplicationController
2 include PinnedToDefaultLocale 2 include PinnedToDefaultLocale
3 include RoleRequired
3 4
4 # Private 5 # Private
5 6
6 before_action :login_required 7 before_action :login_required
8 before_action :require_redaktion
7 9
8 layout 'admin' 10 layout 'admin'
9 11
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 7bf23f17..052b2928 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -1,11 +1,13 @@
1class UsersController < ApplicationController 1class UsersController < ApplicationController
2 include PinnedToDefaultLocale 2 include PinnedToDefaultLocale
3 include RoleRequired
3 4
4 # Private 5 # Private
5 6
6 before_action :login_required 7 before_action :login_required
7 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]
8 before_action :verify_status, :except => [:index, :show] 9 before_action :require_admin, :only => [:index, :new, :create, :reset_otp, :deactivate, :reactivate]
10 before_action :verify_status, :except => [:index]
9 11
10 layout 'admin' 12 layout 'admin'
11 13
@@ -54,8 +56,6 @@ class UsersController < ApplicationController
54 end 56 end
55 57
56 def deactivate 58 def deactivate
57 return deny_user_access unless current_user.is_admin?
58
59 if @user == current_user 59 if @user == current_user
60 flash[:error] = t("flash.users.cannot_deactivate_self") 60 flash[:error] = t("flash.users.cannot_deactivate_self")
61 elsif @user.deactivate!(:actor => current_user) 61 elsif @user.deactivate!(:actor => current_user)
@@ -66,8 +66,6 @@ class UsersController < ApplicationController
66 end 66 end
67 67
68 def reactivate 68 def reactivate
69 return deny_user_access unless current_user.is_admin?
70
71 if @user.reactivate!(:actor => current_user) 69 if @user.reactivate!(:actor => current_user)
72 flash[:notice] = t("flash.users.reactivated", :login => @user.login) 70 flash[:notice] = t("flash.users.reactivated", :login => @user.login)
73 end 71 end
@@ -76,7 +74,6 @@ class UsersController < ApplicationController
76 end 74 end
77 75
78 def reset_otp 76 def reset_otp
79 return deny_user_access unless current_user.admin?
80 @user.disable_otp!(:actor => current_user) 77 @user.disable_otp!(:actor => current_user)
81 flash[:notice] = t("flash.users.otp_reset", :login => @user.login) 78 flash[:notice] = t("flash.users.otp_reset", :login => @user.login)
82 redirect_to edit_user_path(@user) 79 redirect_to edit_user_path(@user)
@@ -106,7 +103,6 @@ class UsersController < ApplicationController
106 end 103 end
107 104
108 def deny_user_access 105 def deny_user_access
109 flash[:notice] = t("flash.common.admin_required") 106 deny_role_access(:admin_required)
110 redirect_to users_path
111 end 107 end
112end 108end
diff --git a/app/views/admin/index.html.erb b/app/views/admin/index.html.erb
index 984858e5..e3591c4e 100644
--- a/app/views/admin/index.html.erb
+++ b/app/views/admin/index.html.erb
@@ -71,11 +71,15 @@
71 <%= link_to assets_path, class: "action_button" do %> 71 <%= link_to assets_path, class: "action_button" do %>
72 <%= icon("folder", library: "tabler", "aria-hidden": true) %> <%= t("assets.index.title") %> 72 <%= icon("folder", library: "tabler", "aria-hidden": true) %> <%= t("assets.index.title") %>
73 <% end %> 73 <% end %>
74 <%= link_to users_path, class: "action_button" do %> 74 <% if current_user.is_admin? %>
75 <%= icon("users", library: "tabler", "aria-hidden": true) %> <%= t("users.index.users") %> 75 <%= link_to users_path, class: "action_button" do %>
76 <%= icon("users", library: "tabler", "aria-hidden": true) %> <%= t("users.index.title") %>
77 <% end %>
76 <% end %> 78 <% end %>
77 <%= link_to menu_items_path, class: "action_button" do %> 79 <% if current_user.redaktion? %>
78 <%= icon("menu-2", library: "tabler", "aria-hidden": true) %> <%= t(".navigation") %> 80 <%= link_to menu_items_path, class: "action_button" do %>
81 <%= icon("menu-2", library: "tabler", "aria-hidden": true) %> <%= t(".navigation") %>
82 <% end %>
79 <% end %> 83 <% end %>
80 <% trash_count = Node.trash.children.count %> 84 <% trash_count = Node.trash.children.count %>
81 <%= link_to trashed_nodes_path, class: "action_button" do %> 85 <%= link_to trashed_nodes_path, class: "action_button" do %>