##// END OF EJS Templates
Set a default timeout value (#19851)....
Jean-Philippe Lang -
r13972:752c414e8abd
parent child
Show More
@@ -1,223 +1,224
1 require 'active_support/core_ext/object/to_query'
1 require 'active_support/core_ext/object/to_query'
2 require 'rack/utils'
2 require 'rack/utils'
3
3
4 module Redmine
4 module Redmine
5 module SudoMode
5 module SudoMode
6
6
7 class SudoRequired < StandardError
7 class SudoRequired < StandardError
8 end
8 end
9
9
10
10
11 class Form
11 class Form
12 include ActiveModel::Validations
12 include ActiveModel::Validations
13
13
14 attr_accessor :password, :original_fields
14 attr_accessor :password, :original_fields
15 validate :check_password
15 validate :check_password
16
16
17 def initialize(password = nil)
17 def initialize(password = nil)
18 self.password = password
18 self.password = password
19 end
19 end
20
20
21 def check_password
21 def check_password
22 unless password.present? && User.current.check_password?(password)
22 unless password.present? && User.current.check_password?(password)
23 errors[:password] << :invalid
23 errors[:password] << :invalid
24 end
24 end
25 end
25 end
26 end
26 end
27
27
28
28
29 module Helper
29 module Helper
30 # Represents params data from hash as hidden fields
30 # Represents params data from hash as hidden fields
31 #
31 #
32 # taken from https://github.com/brianhempel/hash_to_hidden_fields
32 # taken from https://github.com/brianhempel/hash_to_hidden_fields
33 def hash_to_hidden_fields(hash)
33 def hash_to_hidden_fields(hash)
34 cleaned_hash = hash.reject { |k, v| v.nil? }
34 cleaned_hash = hash.reject { |k, v| v.nil? }
35 pairs = cleaned_hash.to_query.split(Rack::Utils::DEFAULT_SEP)
35 pairs = cleaned_hash.to_query.split(Rack::Utils::DEFAULT_SEP)
36 tags = pairs.map do |pair|
36 tags = pairs.map do |pair|
37 key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) }
37 key, value = pair.split('=', 2).map { |str| Rack::Utils.unescape(str) }
38 hidden_field_tag(key, value)
38 hidden_field_tag(key, value)
39 end
39 end
40 tags.join("\n").html_safe
40 tags.join("\n").html_safe
41 end
41 end
42 end
42 end
43
43
44
44
45 module Controller
45 module Controller
46 extend ActiveSupport::Concern
46 extend ActiveSupport::Concern
47
47
48 included do
48 included do
49 around_filter :sudo_mode
49 around_filter :sudo_mode
50 end
50 end
51
51
52 # Sudo mode Around Filter
52 # Sudo mode Around Filter
53 #
53 #
54 # Checks the 'last used' timestamp from session and sets the
54 # Checks the 'last used' timestamp from session and sets the
55 # SudoMode::active? flag accordingly.
55 # SudoMode::active? flag accordingly.
56 #
56 #
57 # After the request refreshes the timestamp if sudo mode was used during
57 # After the request refreshes the timestamp if sudo mode was used during
58 # this request.
58 # this request.
59 def sudo_mode
59 def sudo_mode
60 if sudo_timestamp_valid?
60 if sudo_timestamp_valid?
61 SudoMode.active!
61 SudoMode.active!
62 end
62 end
63 yield
63 yield
64 update_sudo_timestamp! if SudoMode.was_used?
64 update_sudo_timestamp! if SudoMode.was_used?
65 end
65 end
66
66
67 # This renders the sudo mode form / handles sudo form submission.
67 # This renders the sudo mode form / handles sudo form submission.
68 #
68 #
69 # Call this method in controller actions if sudo permissions are required
69 # Call this method in controller actions if sudo permissions are required
70 # for processing this request. This approach is good in cases where the
70 # for processing this request. This approach is good in cases where the
71 # action needs to be protected in any case or where the check is simple.
71 # action needs to be protected in any case or where the check is simple.
72 #
72 #
73 # In cases where this decision depends on complex conditions in the model,
73 # In cases where this decision depends on complex conditions in the model,
74 # consider the declarative approach using the require_sudo_mode class
74 # consider the declarative approach using the require_sudo_mode class
75 # method and a corresponding declaration in the model that causes it to throw
75 # method and a corresponding declaration in the model that causes it to throw
76 # a SudoRequired Error when necessary.
76 # a SudoRequired Error when necessary.
77 #
77 #
78 # All parameter names given are included as hidden fields to be resubmitted
78 # All parameter names given are included as hidden fields to be resubmitted
79 # along with the password.
79 # along with the password.
80 #
80 #
81 # Returns true when processing the action should continue, false otherwise.
81 # Returns true when processing the action should continue, false otherwise.
82 # If false is returned, render has already been called for display of the
82 # If false is returned, render has already been called for display of the
83 # password form.
83 # password form.
84 #
84 #
85 # if @user.mail_changed?
85 # if @user.mail_changed?
86 # require_sudo_mode :user or return
86 # require_sudo_mode :user or return
87 # end
87 # end
88 #
88 #
89 def require_sudo_mode(*param_names)
89 def require_sudo_mode(*param_names)
90 return true if SudoMode.active?
90 return true if SudoMode.active?
91
91
92 if param_names.blank?
92 if param_names.blank?
93 param_names = params.keys - %w(id action controller sudo_password)
93 param_names = params.keys - %w(id action controller sudo_password)
94 end
94 end
95
95
96 process_sudo_form
96 process_sudo_form
97
97
98 if SudoMode.active?
98 if SudoMode.active?
99 true
99 true
100 else
100 else
101 render_sudo_form param_names
101 render_sudo_form param_names
102 false
102 false
103 end
103 end
104 end
104 end
105
105
106 # display the sudo password form
106 # display the sudo password form
107 def render_sudo_form(param_names)
107 def render_sudo_form(param_names)
108 @sudo_form ||= SudoMode::Form.new
108 @sudo_form ||= SudoMode::Form.new
109 @sudo_form.original_fields = params.slice( *param_names )
109 @sudo_form.original_fields = params.slice( *param_names )
110 # a simple 'render "sudo_mode/new"' works when used directly inside an
110 # a simple 'render "sudo_mode/new"' works when used directly inside an
111 # action, but not when called from a before_filter:
111 # action, but not when called from a before_filter:
112 respond_to do |format|
112 respond_to do |format|
113 format.html { render 'sudo_mode/new' }
113 format.html { render 'sudo_mode/new' }
114 format.js { render 'sudo_mode/new' }
114 format.js { render 'sudo_mode/new' }
115 end
115 end
116 end
116 end
117
117
118 # handle sudo password form submit
118 # handle sudo password form submit
119 def process_sudo_form
119 def process_sudo_form
120 if params[:sudo_password]
120 if params[:sudo_password]
121 @sudo_form = SudoMode::Form.new(params[:sudo_password])
121 @sudo_form = SudoMode::Form.new(params[:sudo_password])
122 if @sudo_form.valid?
122 if @sudo_form.valid?
123 SudoMode.active!
123 SudoMode.active!
124 else
124 else
125 flash.now[:error] = l(:notice_account_wrong_password)
125 flash.now[:error] = l(:notice_account_wrong_password)
126 end
126 end
127 end
127 end
128 end
128 end
129
129
130 def sudo_timestamp_valid?
130 def sudo_timestamp_valid?
131 session[:sudo_timestamp].to_i > SudoMode.timeout.ago.to_i
131 session[:sudo_timestamp].to_i > SudoMode.timeout.ago.to_i
132 end
132 end
133
133
134 def update_sudo_timestamp!(new_value = Time.now.to_i)
134 def update_sudo_timestamp!(new_value = Time.now.to_i)
135 session[:sudo_timestamp] = new_value
135 session[:sudo_timestamp] = new_value
136 end
136 end
137
137
138 # Before Filter which is used by the require_sudo_mode class method.
138 # Before Filter which is used by the require_sudo_mode class method.
139 class SudoRequestFilter < Struct.new(:parameters, :request_methods)
139 class SudoRequestFilter < Struct.new(:parameters, :request_methods)
140 def before(controller)
140 def before(controller)
141 method_matches = request_methods.blank? || request_methods.include?(controller.request.method_symbol)
141 method_matches = request_methods.blank? || request_methods.include?(controller.request.method_symbol)
142 if controller.api_request?
142 if controller.api_request?
143 true
143 true
144 elsif SudoMode.possible? && method_matches
144 elsif SudoMode.possible? && method_matches
145 controller.require_sudo_mode( *parameters )
145 controller.require_sudo_mode( *parameters )
146 else
146 else
147 true
147 true
148 end
148 end
149 end
149 end
150 end
150 end
151
151
152 module ClassMethods
152 module ClassMethods
153
153
154 # Handles sudo requirements for the given actions, preserving the named
154 # Handles sudo requirements for the given actions, preserving the named
155 # parameters, or any parameters if you omit the :parameters option.
155 # parameters, or any parameters if you omit the :parameters option.
156 #
156 #
157 # Sudo enforcement by default is active for all requests to an action
157 # Sudo enforcement by default is active for all requests to an action
158 # but may be limited to a certain subset of request methods via the
158 # but may be limited to a certain subset of request methods via the
159 # :only option.
159 # :only option.
160 #
160 #
161 # Examples:
161 # Examples:
162 #
162 #
163 # require_sudo_mode :account, only: :post
163 # require_sudo_mode :account, only: :post
164 # require_sudo_mode :update, :create, parameters: %w(role)
164 # require_sudo_mode :update, :create, parameters: %w(role)
165 # require_sudo_mode :destroy
165 # require_sudo_mode :destroy
166 #
166 #
167 def require_sudo_mode(*args)
167 def require_sudo_mode(*args)
168 actions = args.dup
168 actions = args.dup
169 options = actions.extract_options!
169 options = actions.extract_options!
170 filter = SudoRequestFilter.new Array(options[:parameters]), Array(options[:only])
170 filter = SudoRequestFilter.new Array(options[:parameters]), Array(options[:only])
171 before_filter filter, only: actions
171 before_filter filter, only: actions
172 end
172 end
173 end
173 end
174 end
174 end
175
175
176
176
177 # true if the sudo mode state was queried during this request
177 # true if the sudo mode state was queried during this request
178 def self.was_used?
178 def self.was_used?
179 !!RequestStore.store[:sudo_mode_was_used]
179 !!RequestStore.store[:sudo_mode_was_used]
180 end
180 end
181
181
182 # true if sudo mode is currently active.
182 # true if sudo mode is currently active.
183 #
183 #
184 # Calling this method also turns was_used? to true, therefore
184 # Calling this method also turns was_used? to true, therefore
185 # it is important to only call this when sudo is actually needed, as the last
185 # it is important to only call this when sudo is actually needed, as the last
186 # condition to determine wether a change can be done or not.
186 # condition to determine wether a change can be done or not.
187 #
187 #
188 # If you do it wrong, timeout of the sudo mode will happen too late or not at
188 # If you do it wrong, timeout of the sudo mode will happen too late or not at
189 # all.
189 # all.
190 def self.active?
190 def self.active?
191 if !!RequestStore.store[:sudo_mode]
191 if !!RequestStore.store[:sudo_mode]
192 RequestStore.store[:sudo_mode_was_used] = true
192 RequestStore.store[:sudo_mode_was_used] = true
193 end
193 end
194 end
194 end
195
195
196 def self.active!
196 def self.active!
197 RequestStore.store[:sudo_mode] = true
197 RequestStore.store[:sudo_mode] = true
198 end
198 end
199
199
200 def self.possible?
200 def self.possible?
201 enabled? && User.current.logged?
201 enabled? && User.current.logged?
202 end
202 end
203
203
204 # Turn off sudo mode (never require password entry).
204 # Turn off sudo mode (never require password entry).
205 def self.disable!
205 def self.disable!
206 RequestStore.store[:sudo_mode_disabled] = true
206 RequestStore.store[:sudo_mode_disabled] = true
207 end
207 end
208
208
209 # Turn sudo mode back on
209 # Turn sudo mode back on
210 def self.enable!
210 def self.enable!
211 RequestStore.store[:sudo_mode_disabled] = nil
211 RequestStore.store[:sudo_mode_disabled] = nil
212 end
212 end
213
213
214 def self.enabled?
214 def self.enabled?
215 Redmine::Configuration['sudo_mode'] && !RequestStore.store[:sudo_mode_disabled]
215 Redmine::Configuration['sudo_mode'] && !RequestStore.store[:sudo_mode_disabled]
216 end
216 end
217
217
218 # Timespan after which sudo mode expires when unused.
218 # Timespan after which sudo mode expires when unused.
219 def self.timeout
219 def self.timeout
220 Redmine::Configuration['sudo_mode_timeout'].to_i.minutes
220 m = Redmine::Configuration['sudo_mode_timeout'].to_i
221 (m > 0 ? m : 15).minutes
221 end
222 end
222 end
223 end
223 end
224 end
General Comments 0
You need to be logged in to leave comments. Login now