summaryrefslogtreecommitdiff
path: root/app/controllers
diff options
context:
space:
mode:
Diffstat (limited to 'app/controllers')
-rw-r--r--app/controllers/concerns/role_required.rb9
-rw-r--r--app/controllers/elevations_controller.rb44
-rw-r--r--app/controllers/otp_challenges_controller.rb3
-rw-r--r--app/controllers/users_controller.rb3
4 files changed, 58 insertions, 1 deletions
diff --git a/app/controllers/concerns/role_required.rb b/app/controllers/concerns/role_required.rb
index b841b8cc..22ce625e 100644
--- a/app/controllers/concerns/role_required.rb
+++ b/app/controllers/concerns/role_required.rb
@@ -20,4 +20,13 @@ module RoleRequired
20 flash[:error] = t("flash.common.#{key}") 20 flash[:error] = t("flash.common.#{key}")
21 redirect_to admin_path 21 redirect_to admin_path
22 end 22 end
23
24 # require_admin must precede this in the filter chain, so a non-admin is
25 # denied by role before elevation is ever considered.
26 def require_elevation
27 return if elevated?
28
29 session[:elevation_return_to] = request.fullpath
30 redirect_to new_elevation_path
31 end
23end 32end
diff --git a/app/controllers/elevations_controller.rb b/app/controllers/elevations_controller.rb
new file mode 100644
index 00000000..804b8f95
--- /dev/null
+++ b/app/controllers/elevations_controller.rb
@@ -0,0 +1,44 @@
1# Step-up authentication for janitorial work. Mirrors the two-step login's
2# attempt cap: verify_otp! guards replay but not rate, and an attacker with a
3# stolen cookie would otherwise have unlimited tries at a six-digit code.
4class ElevationsController < ApplicationController
5 include RoleRequired
6
7 layout 'admin'
8
9 before_action :login_required
10 before_action :require_admin
11
12 MAX_ATTEMPTS = 5
13
14 def new
15 redirect_to admin_path if elevated?
16 end
17
18 def create
19 return render(:new) unless current_user.otp_enrolled?
20
21 session[:elevation_attempts] = session[:elevation_attempts].to_i + 1
22 if session[:elevation_attempts] > MAX_ATTEMPTS
23 logout_killing_session!
24 flash[:error] = t("flash.otp.too_many_attempts")
25 return redirect_to(login_path)
26 end
27
28 if current_user.verify_otp!(params[:code])
29 session.delete(:elevation_attempts)
30 elevate!
31 redirect_to safe_return_to(session.delete(:elevation_return_to),
32 :default => users_path)
33 else
34 flash.now[:error] = t("flash.otp.code_mismatch")
35 render :new
36 end
37 end
38
39 def destroy
40 drop_elevation!
41 flash[:notice] = t("flash.elevation.dropped")
42 redirect_to admin_path
43 end
44end
diff --git a/app/controllers/otp_challenges_controller.rb b/app/controllers/otp_challenges_controller.rb
index eeaeac20..87586241 100644
--- a/app/controllers/otp_challenges_controller.rb
+++ b/app/controllers/otp_challenges_controller.rb
@@ -28,6 +28,9 @@ class OtpChallengesController < ApplicationController
28 reset_session 28 reset_session
29 self.current_user = user 29 self.current_user = user
30 session[:logged_in_at] = Time.now.to_i 30 session[:logged_in_at] = Time.now.to_i
31 # an admin who logs in and goes straight to user management
32 # is already elevated
33 elevate! if user.is_admin?
31 flash[:notice] = t("flash.common.logged_in") 34 flash[:notice] = t("flash.common.logged_in")
32 redirect_to safe_return_to(return_to, :default => admin_path) 35 redirect_to safe_return_to(return_to, :default => admin_path)
33 else 36 else
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 052b2928..583ebac0 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -7,6 +7,7 @@ class UsersController < ApplicationController
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]
9 before_action :require_admin, :only => [:index, :new, :create, :reset_otp, :deactivate, :reactivate] 9 before_action :require_admin, :only => [:index, :new, :create, :reset_otp, :deactivate, :reactivate]
10 before_action :require_elevation, :only => [:new, :create, :reset_otp, :deactivate, :reactivate]
10 before_action :verify_status, :except => [:index] 11 before_action :verify_status, :except => [:index]
11 12
12 layout 'admin' 13 layout 'admin'
@@ -87,7 +88,7 @@ class UsersController < ApplicationController
87 :roles => []) 88 :roles => [])
88 # Checkbox arrays post a leading blank from the hidden field. 89 # Checkbox arrays post a leading blank from the hidden field.
89 permitted[:roles] = Array(permitted[:roles]).reject(&:blank?) if permitted.key?(:roles) 90 permitted[:roles] = Array(permitted[:roles]).reject(&:blank?) if permitted.key?(:roles)
90 permitted.delete(:roles) unless current_user.is_admin? 91 permitted.delete(:roles) unless current_user.is_admin? && elevated?
91 permitted 92 permitted
92 end 93 end
93 94