lockdown 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,220 @@
1
+ module Lockdown
2
+ module Controller#:nodoc:
3
+ #
4
+ # Core Controller locking methods
5
+ #
6
+ module Core
7
+ def self.included(base)
8
+ base.send :include, Lockdown::Controller::Core::InstanceMethods
9
+ end
10
+
11
+ module InstanceMethods
12
+ def configure_lock_down
13
+ check_session_expiry
14
+ store_location
15
+ end
16
+
17
+ def set_current_user
18
+ login_from_basic_auth? unless logged_in?
19
+ if logged_in?
20
+ Thread.current[:profile_id] = current_profile_id
21
+ Thread.current[:client_id] = current_client_id if respond_to? :current_client_id
22
+ end
23
+ end
24
+
25
+ def check_request_authorization
26
+ unless authorized?(path_from_hash(params))
27
+ raise SecurityError, "Authorization failed for params #{params.inspect}"
28
+ end
29
+ end
30
+
31
+ def redirect_back_or_default(default)
32
+ session[:prevpage] ? send_to(session[:prevpage]) : send_to(default)
33
+ end
34
+
35
+ private
36
+
37
+ def path_allowed?(url)
38
+ req = Lockdown.format_controller_action(url)
39
+ session[:access_rights] ||= Lockdown::UserGroups[:public_access]
40
+ session[:access_rights].each do |ar|
41
+ return true if req =~ /#{ar}$/
42
+ end
43
+ false
44
+ end
45
+
46
+ def check_session_expiry
47
+ if session[:expiry_time] && session[:expiry_time] < Time.now
48
+ nil_lockdown_values
49
+ end
50
+ session[:expiry_time] = Time.now + Lockdown::SESSION_TIMEOUT
51
+ end
52
+
53
+ def store_location
54
+ if request.method == :get && !(session[:thispage] == sent_from_uri)
55
+ session[:prevpage] = session[:thispage] || ''
56
+ session[:thispage] = sent_from_uri
57
+ end
58
+ end
59
+
60
+ # Called from current_user. Now, attempt to login by
61
+ # basic authentication information.
62
+ def login_from_basic_auth?
63
+ username, passwd = get_auth_data
64
+ if username && passwd
65
+ set_session_user User.authenticate(username, passwd)
66
+ end
67
+ end
68
+
69
+ @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
70
+ # gets BASIC auth info
71
+ def get_auth_data
72
+ auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
73
+ auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
74
+ return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
75
+ end
76
+ end # InstanceMethods
77
+ end # Core
78
+
79
+ #
80
+ # Merb Controller locking methods
81
+ #
82
+ module Merb
83
+ def self.included(base)
84
+ base.send :include, Lockdown::Controller::Merb::InstanceMethods
85
+
86
+ base.before :set_current_user
87
+ base.before :configure_lock_down
88
+ base.before :check_request_authorization
89
+ end
90
+
91
+ module InstanceMethods
92
+ def self.included(base)
93
+ base.class_eval do
94
+ alias :send_to :redirect
95
+ end
96
+ base.send :include, Lockdown::Controller::Core
97
+ end
98
+
99
+ def sent_from_uri
100
+ request.uri
101
+ end
102
+
103
+ def authorized?(path)
104
+ return true if current_user_is_admin?
105
+
106
+ # See if path is known
107
+ return true if path_allowed?(path)
108
+
109
+ return false
110
+ end
111
+
112
+ # Can log Error => e if desired, I don't desire to now.
113
+ # For now, just send home, but will probably make this configurable
114
+ def access_denied(e)
115
+ send_to "/"
116
+ end
117
+
118
+ def path_from_hash(hsh)
119
+ return hsh if hsh.is_a?(String)
120
+ hsh = hsh.to_hash if hsh.is_a?(Mash)
121
+ hsh['controller'].to_s + "/" + hsh['action'].to_s
122
+ end
123
+
124
+ end # InstanceMethods
125
+ end # Merb
126
+
127
+ #
128
+ # Rails Controller locking methods
129
+ #
130
+ module Rails
131
+ def self.included(base)
132
+ base.send :include, Lockdown::Controller::Rails::InstanceMethods
133
+
134
+ base.before_filter do |controller|
135
+ controller.set_current_user
136
+ controller.configure_lock_down
137
+ controller.check_request_authorization
138
+ end
139
+
140
+ base.send :helper_method, :authorized?
141
+
142
+ base.filter_parameter_logging :password, :password_confirmation
143
+
144
+ base.rescue_from SecurityError,
145
+ :with => proc{|e| access_denied(e)}
146
+ end
147
+
148
+ module InstanceMethods
149
+ def self.included(base)
150
+ base.class_eval do
151
+ alias :send_to :redirect_to
152
+ end
153
+ base.send :include, Lockdown::Controller::Core
154
+ end
155
+
156
+ def sent_from_uri
157
+ request.request_uri
158
+ end
159
+
160
+ def authorized?(options)
161
+ return true if current_user_is_admin?
162
+
163
+ url_parts = URI::split url_for(options)
164
+
165
+ path = url_parts[5]
166
+
167
+ # See if path is known
168
+ return true if path_allowed?(path)
169
+
170
+ if options.is_a?(String)
171
+ # Test for a named routed
172
+ begin
173
+ hsh = ActionController::Routing::Routes.recognize_path(options)
174
+ return true if path_allowed?(path_from_hash(hsh)) unless hsh.nil?
175
+ rescue Exception => e
176
+ # continue on
177
+ end
178
+ end
179
+
180
+ # Test to see if using a get method (show)
181
+ path += "/show" if path.split("/").last.to_i > 0
182
+
183
+ return true if path_allowed?(path)
184
+
185
+ return false
186
+ end
187
+
188
+ def access_denied(e)
189
+ reset_session
190
+ respond_to do |accepts|
191
+ accepts.html do
192
+ store_location
193
+ send_to login_path
194
+ end
195
+ accepts.xml do
196
+ headers["Status"] = "Unauthorized"
197
+ headers["WWW-Authenticate"] = %(Basic realm="Web Password")
198
+ render :text => e.message, :status => "401 Unauthorized"
199
+ end
200
+ end
201
+ false
202
+ end
203
+
204
+ def path_from_hash(hsh)
205
+ hsh[:controller].to_s + "/" + hsh[:action].to_s
206
+ end
207
+
208
+ end # InstanceMethods
209
+ end # Rails
210
+
211
+
212
+ end # Controller
213
+ end # Lockdown
214
+
215
+ if Lockdown.merb_app?
216
+ Merb::Controller.send :include, Lockdown::Controller::Merb
217
+ elsif Lockdown.rails_app?
218
+ ActionController::Base.send :include, Lockdown::Controller::Rails
219
+ end
220
+
@@ -0,0 +1,214 @@
1
+ require File.join(File.dirname(__FILE__), "helper") unless Lockdown.const_defined?("Helper")
2
+
3
+ module Lockdown
4
+ module ControllerInspector
5
+ def self.included(base)
6
+ if Lockdown.merb_app?
7
+ base.send :include, Lockdown::ControllerInspector::Merb
8
+ elsif Lockdown.rails_app?
9
+ base.send :include, Lockdown::ControllerInspector::Rails
10
+ end
11
+ end
12
+
13
+ module Core
14
+ include Lockdown::Helper
15
+ #
16
+ # *syms is a splat of controller symbols,
17
+ # e.g all_methods(:users, :authors, :books)
18
+ #
19
+ def all_methods(*syms)
20
+ syms.collect{ |sym| paths_for(sym) }.flatten
21
+ end
22
+
23
+ #
24
+ # controller name (sym) and a splat of methods to
25
+ # exclude from result
26
+ #
27
+ # All user methods except destroy:
28
+ # e.g all_except_methods(:users, :destroy)
29
+ #
30
+ def all_except_methods(sym, *methods)
31
+ paths_for(sym) - paths_for(sym, *methods)
32
+ end
33
+
34
+ #
35
+ # controller name (sym) and a splat of methods to
36
+ # to build the result
37
+ #
38
+ # Only user methods index (list), show (good for readonly access):
39
+ # e.g only_methods(:users, :index, :show)
40
+ #
41
+ def only_methods(sym, *methods)
42
+ paths_for(sym, *methods)
43
+ end
44
+
45
+ #
46
+ # all controllers, all actions
47
+ #
48
+ # This is admin access
49
+ #
50
+ def all_controllers
51
+ controllers = find_all_controller_classes
52
+
53
+ controllers.collect do |controller|
54
+ methods = available_actions(controller)
55
+ paths_for(controller_name(controller), methods)
56
+ end.flatten!
57
+ end
58
+
59
+ private
60
+
61
+ def paths_for(sym_str, *methods)
62
+ str = sym_str.to_s if sym_str.is_a?(Symbol)
63
+ if methods.empty?
64
+ klass = get_controller_class(str)
65
+ methods = available_actions(klass)
66
+ end
67
+ methods.collect{|meth| ctr_path(str) + "/" + meth.to_s }
68
+ end
69
+
70
+ def get_controller_class(str)
71
+ load_controller(str)
72
+ lockdown_const_get(str)
73
+ end
74
+
75
+ def find_all_controller_classes
76
+ load_all_controllers
77
+ return ObjectSpace.controller_classes
78
+ end
79
+
80
+ def ObjectSpace.controller_classes
81
+ subclasses = []
82
+ self.each_object(Class) do |klass|
83
+ subclasses << klass if klass.ancestors.include?(Lockdown.controller_parent)
84
+ end
85
+ subclasses
86
+ end
87
+
88
+ def load_controller(str)
89
+ unless lockdown_const_defined?("Application")
90
+ require(Lockdown.project_root + "/app/controllers/application.rb")
91
+ end
92
+
93
+ unless lockdown_const_defined?(kontroller_class_name(str))
94
+ require(Lockdown.project_root + "/app/controllers/#{kontroller_file_name(str)}")
95
+ end
96
+ end
97
+
98
+ def load_all_controllers
99
+ Dir["#{Lockdown.project_root}/app/controllers/**/*.rb"].sort.each do |c|
100
+ require(c) unless c == "application.rb"
101
+ end
102
+ end
103
+
104
+ def lockdown_const_defined?(str)
105
+ if str.include?("__")
106
+ # this is a namespaced controller. need to apply const_defined_to the namespace
107
+ parts = str.split("__")
108
+ eval("#{camelize(parts[0])}.const_defined?(\"#{kontroller_class_name(parts[1])}\")")
109
+ else
110
+ const_defined?(camelize(str))
111
+ end
112
+ end
113
+
114
+ def lockdown_const_get(str)
115
+ if str.include?("__")
116
+ # this is a namespaced controller. need to apply const_get the namespace
117
+ parts = str.split("__")
118
+ eval("#{camelize(parts[0])}.const_get(\"#{kontroller_class_name(parts[1])}\")")
119
+ else
120
+ const_get(kontroller_class_name(str))
121
+ end
122
+ end
123
+
124
+ def ctr_path(str)
125
+ str.gsub("__","\/")
126
+ end
127
+
128
+ #
129
+ # Convert the str parameter (originally the symbol) to the
130
+ # class name.
131
+ #
132
+ # For a controller defined as :users in access.rb, the str
133
+ # parameter here would be "users". The result of this method
134
+ # would be "/users"
135
+ #
136
+ # For a namespaced controller:
137
+ # In access.rb it would be defined as :admin__users.
138
+ # The str paramter would be "admin__users".
139
+ # The result would be "/admin/users".
140
+ #
141
+ def controller_file_name(str)
142
+ if str.include?("__")
143
+ str.split("__").join("/")
144
+ else
145
+ str
146
+ end
147
+ end
148
+
149
+ #
150
+ # Convert the str parameter (originally the symbol) to the
151
+ # class name.
152
+ #
153
+ # For a controller defined as :users in access.rb, the str
154
+ # parameter here would be "users". The result of this method
155
+ # would be "Users"
156
+ #
157
+ def controller_class_name(str)
158
+ if str.include?("__")
159
+ str.split("__").collect{|p| camelize(p)}.join("::")
160
+ else
161
+ camelize(str)
162
+ end
163
+ end
164
+
165
+ #
166
+ # The reverse of controller_class_name. Convert the controllers
167
+ # class name to the string version of the symbols used in acces.rb.
168
+ #
169
+ # For a controller defined as :users in access.rb, the klass
170
+ # parameter here would be Users (the class). The result of this method
171
+ # would be "users", the string version of :users.
172
+ #
173
+ # Luckily both Rails and Merb have the controller_name method. This
174
+ # is here in case that changes.
175
+ #
176
+ def controller_name(klass)
177
+ klass.controller_name
178
+ end
179
+ end #Core
180
+
181
+ module Rails #:nodoc:
182
+ include Lockdown::ControllerInspector::Core
183
+
184
+ def kontroller_class_name(str)
185
+ "#{controller_class_name(str)}Controller"
186
+ end
187
+
188
+ def kontroller_file_name(str)
189
+ "#{controller_file_name(str)}_controller.rb"
190
+ end
191
+
192
+ def available_actions(klass)
193
+ klass.public_instance_methods - klass.hidden_actions
194
+ end
195
+ end # Rails
196
+
197
+ module Merb #:nodoc:
198
+ include Lockdown::ControllerInspector::Core
199
+
200
+ def kontroller_class_name(str)
201
+ controller_class_name(str)
202
+ end
203
+
204
+ def kontroller_file_name(str)
205
+ controller_file_name(str) + ".rb"
206
+ end
207
+
208
+ def available_actions(klass)
209
+ klass.callable_actions.keys
210
+ end
211
+
212
+ end # Merb
213
+ end # ControllerInspector
214
+ end # Lockdown
@@ -0,0 +1,53 @@
1
+ module Lockdown
2
+ module Helper
3
+ def syms_from_names(ary)
4
+ rvalue = []
5
+ ary.each{|ar| rvalue << symbolize(ar.name)}
6
+ rvalue
7
+ end
8
+
9
+ #
10
+ # If str_sym is a Symbol (:users), give me back "Users"
11
+ # If str_sym is a String ("Users"), give me back :users
12
+ #
13
+ # Was :to_title_sym for String and :to_title_str for Symbol
14
+ #
15
+ def convert_reference_name(str_sym)
16
+ if str_sym.is_a?(Symbol)
17
+ titleize(str_sym)
18
+ else
19
+ underscore(str_sym).tr(' ','_').to_sym
20
+ end
21
+ end
22
+
23
+ def symbolize(str)
24
+ str.downcase.gsub("admin ","admin__").gsub(" ","_").to_sym
25
+ end
26
+
27
+ def camelize(str)
28
+ str.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
29
+ end
30
+
31
+
32
+ def random_string(len = 10)
33
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
34
+ Array.new(len){||chars[rand(chars.size)]}.join
35
+ end
36
+
37
+ private
38
+ def titleize(str)
39
+ humanize(underscore(str)).gsub(/\b([a-z])/) { $1.capitalize }
40
+ end
41
+
42
+ def humanize(str)
43
+ str.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
44
+ end
45
+
46
+ def underscore(str)
47
+ str.to_s.gsub(/::/, '/').
48
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
49
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
50
+ tr("-", "_").downcase
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,40 @@
1
+ require File.join(File.dirname(__FILE__), "helper") unless Lockdown.const_defined?("Helper")
2
+
3
+ module Lockdown
4
+ module Model
5
+ def self.included(base)
6
+ base.send :include, Lockdown::Model::InstanceMethods
7
+ end
8
+
9
+ module InstanceMethods
10
+ def self.included(base)
11
+ base.class_eval do
12
+ alias :create_without_stamps :create
13
+ alias :update_without_stamps :update
14
+ end
15
+ end
16
+
17
+ def current_profile_id
18
+ Thread.current[:profile_id]
19
+ end
20
+
21
+
22
+ def create_with_stamps
23
+ profile_id = current_profile_id || Profile::SYSTEM
24
+ self[:created_by] = profile_id if self.respond_to?(:created_by)
25
+ self[:updated_by] = profile_id if self.respond_to?(:updated_by)
26
+ create_without_stamps
27
+ end
28
+ alias :create :create_with_stamps
29
+
30
+ def update_with_stamps
31
+ profile_id = current_profile_id || Profile::SYSTEM
32
+ self[:updated_by] = profile_id if self.respond_to?(:updated_by)
33
+ update_without_stamps
34
+ end
35
+ alias :update :update_with_stamps
36
+ end # InstanceMethods
37
+ end # Model
38
+ end # Lockdown
39
+
40
+ Lockdown.orm_parent.send :include, Lockdown::Model
@@ -0,0 +1,9 @@
1
+ module Lockdown #:nodoc:
2
+ module VERSION #:nodoc:
3
+ MAJOR = 0
4
+ MINOR = 1
5
+ TINY = 0
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,82 @@
1
+ module Lockdown
2
+ module View
3
+ module Core
4
+ def links(*lis)
5
+ rvalue = []
6
+ lis.each{|link| rvalue << link if link.length > 0 }
7
+ rvalue.join(" | ")
8
+ end
9
+ end # Core
10
+
11
+ module Merb
12
+ include Lockdown::View::Core
13
+ def self.included(base)
14
+ base.send :alias_method, :merb_link_to, :link_to
15
+ end
16
+
17
+ def link_to(name, url = '', options = {})
18
+ if authorized? url
19
+ return merb_link_to(name, url, options)
20
+ end
21
+ return ""
22
+ end
23
+
24
+ def link_to_or_show(name, url = '', options = {})
25
+ lnk = link_to(name, options, html_options)
26
+ lnk.length == 0 ? name : lnk
27
+ end
28
+ end # Merb
29
+
30
+ module Rails
31
+ include Lockdown::View::Core
32
+ def self.included(base)
33
+ base.send :alias_method, :rails_link_to, :link_to
34
+ base.send :alias_method, :rails_button_to, :button_to
35
+ end
36
+
37
+ def ld_link_to(name, options = {}, html_options = nil)
38
+ url = lock_down_url(options, html_options)
39
+ if authorized? url
40
+ return rails_link_to(name,options,html_options)
41
+ end
42
+ return ""
43
+ end
44
+
45
+ def link_to_or_show(name, options = {}, html_options = nil)
46
+ lnk = link_to(name, options, html_options)
47
+ lnk.length == 0 ? name : lnk
48
+ end
49
+
50
+
51
+ def button_to(name, options = {}, html_options = nil)
52
+ url = lock_down_url(options, html_options)
53
+ if authorized? url
54
+ return rails_button_to(name,options,html_options)
55
+ end
56
+ return ""
57
+ end
58
+
59
+
60
+ private
61
+ def lock_down_url(options, html_options = {})
62
+ return options unless options.respond_to?(:new_record?)
63
+ p = polymorphic_path(options)
64
+ if html_options.is_a?(Hash) && html_options[:method] == :delete
65
+ p += "/destroy"
66
+ elsif p.split("/").last.to_i > 0
67
+ p += "/show"
68
+ end
69
+ return p
70
+ end
71
+ end # Rails
72
+ end # View
73
+ end # Lockdown
74
+
75
+ if Object.const_defined?("Merb") && Merb.const_defined?("AssetsMixin")
76
+ Merb::AssetsMixin.send :include, Lockdown::View::Merb
77
+ elsif Object.const_defined?("ActionView")
78
+ ActionView::Base.send :include, Lockdown::View::Rails
79
+ else
80
+ raise NotImplementedError, "Application helper unknown to Lockdown."
81
+ end
82
+