lockdown 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+