andrewzielinski-lockdown 0.9.6

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.
Files changed (59) hide show
  1. data/History.txt +195 -0
  2. data/README.txt +36 -0
  3. data/Rakefile +41 -0
  4. data/lib/lockdown.rb +70 -0
  5. data/lib/lockdown/context.rb +41 -0
  6. data/lib/lockdown/database.rb +105 -0
  7. data/lib/lockdown/frameworks/rails.rb +146 -0
  8. data/lib/lockdown/frameworks/rails/controller.rb +147 -0
  9. data/lib/lockdown/frameworks/rails/view.rb +61 -0
  10. data/lib/lockdown/helper.rb +95 -0
  11. data/lib/lockdown/orms/active_record.rb +68 -0
  12. data/lib/lockdown/permission.rb +204 -0
  13. data/lib/lockdown/rules.rb +289 -0
  14. data/lib/lockdown/session.rb +57 -0
  15. data/lib/lockdown/system.rb +57 -0
  16. data/rails_generators/lockdown/lockdown_generator.rb +273 -0
  17. data/rails_generators/lockdown/templates/app/controllers/permissions_controller.rb +22 -0
  18. data/rails_generators/lockdown/templates/app/controllers/sessions_controller.rb +39 -0
  19. data/rails_generators/lockdown/templates/app/controllers/user_groups_controller.rb +122 -0
  20. data/rails_generators/lockdown/templates/app/controllers/users_controller.rb +117 -0
  21. data/rails_generators/lockdown/templates/app/helpers/permissions_helper.rb +2 -0
  22. data/rails_generators/lockdown/templates/app/helpers/user_groups_helper.rb +2 -0
  23. data/rails_generators/lockdown/templates/app/helpers/users_helper.rb +2 -0
  24. data/rails_generators/lockdown/templates/app/models/permission.rb +13 -0
  25. data/rails_generators/lockdown/templates/app/models/profile.rb +10 -0
  26. data/rails_generators/lockdown/templates/app/models/user.rb +95 -0
  27. data/rails_generators/lockdown/templates/app/models/user_group.rb +15 -0
  28. data/rails_generators/lockdown/templates/app/views/permissions/index.html.erb +16 -0
  29. data/rails_generators/lockdown/templates/app/views/permissions/show.html.erb +26 -0
  30. data/rails_generators/lockdown/templates/app/views/sessions/new.html.erb +12 -0
  31. data/rails_generators/lockdown/templates/app/views/user_groups/edit.html.erb +33 -0
  32. data/rails_generators/lockdown/templates/app/views/user_groups/index.html.erb +20 -0
  33. data/rails_generators/lockdown/templates/app/views/user_groups/new.html.erb +31 -0
  34. data/rails_generators/lockdown/templates/app/views/user_groups/show.html.erb +29 -0
  35. data/rails_generators/lockdown/templates/app/views/users/edit.html.erb +51 -0
  36. data/rails_generators/lockdown/templates/app/views/users/index.html.erb +22 -0
  37. data/rails_generators/lockdown/templates/app/views/users/new.html.erb +50 -0
  38. data/rails_generators/lockdown/templates/app/views/users/show.html.erb +33 -0
  39. data/rails_generators/lockdown/templates/config/initializers/lockit.rb +1 -0
  40. data/rails_generators/lockdown/templates/db/migrate/create_admin_user.rb +17 -0
  41. data/rails_generators/lockdown/templates/db/migrate/create_permissions.rb +19 -0
  42. data/rails_generators/lockdown/templates/db/migrate/create_profiles.rb +26 -0
  43. data/rails_generators/lockdown/templates/db/migrate/create_user_groups.rb +19 -0
  44. data/rails_generators/lockdown/templates/db/migrate/create_users.rb +17 -0
  45. data/rails_generators/lockdown/templates/lib/lockdown/README +42 -0
  46. data/rails_generators/lockdown/templates/lib/lockdown/init.rb +122 -0
  47. data/spec/lockdown/database_spec.rb +158 -0
  48. data/spec/lockdown/frameworks/rails/controller_spec.rb +224 -0
  49. data/spec/lockdown/frameworks/rails/view_spec.rb +125 -0
  50. data/spec/lockdown/frameworks/rails_spec.rb +175 -0
  51. data/spec/lockdown/permission_spec.rb +156 -0
  52. data/spec/lockdown/rules_spec.rb +109 -0
  53. data/spec/lockdown/session_spec.rb +89 -0
  54. data/spec/lockdown/system_spec.rb +59 -0
  55. data/spec/lockdown_spec.rb +19 -0
  56. data/spec/rcov.opts +5 -0
  57. data/spec/spec.opts +3 -0
  58. data/spec/spec_helper.rb +1 -0
  59. metadata +112 -0
@@ -0,0 +1,146 @@
1
+ require File.join(File.dirname(__FILE__), "rails", "controller")
2
+ require File.join(File.dirname(__FILE__), "rails", "view")
3
+
4
+ module Lockdown
5
+ module Frameworks
6
+ module Rails
7
+ class << self
8
+ def use_me?
9
+ Object.const_defined?("ActionController") && ActionController.const_defined?("Base")
10
+ end
11
+
12
+ def included(mod)
13
+ mod.extend Lockdown::Frameworks::Rails::Environment
14
+ mixin
15
+ end
16
+
17
+ def mixin
18
+ Lockdown.controller_parent.class_eval do
19
+ include Lockdown::Session
20
+ include Lockdown::Frameworks::Rails::Controller::Lock
21
+ end
22
+
23
+ Lockdown.controller_parent.helper_method :authorized?
24
+
25
+ Lockdown.controller_parent.before_filter do |c|
26
+ c.set_current_user
27
+ c.configure_lockdown
28
+ c.check_request_authorization
29
+ end
30
+
31
+ Lockdown.controller_parent.filter_parameter_logging :password,
32
+ :password_confirmation
33
+
34
+ Lockdown.controller_parent.rescue_from SecurityError,
35
+ :with => proc{|e| access_denied(e)}
36
+
37
+ Lockdown.view_helper.class_eval do
38
+ include Lockdown::Frameworks::Rails::View
39
+ end
40
+
41
+ Lockdown::System.class_eval do
42
+ extend Lockdown::Frameworks::Rails::System
43
+ end
44
+ end
45
+ end # class block
46
+
47
+ module Environment
48
+
49
+ def project_root
50
+ ::RAILS_ROOT
51
+ end
52
+
53
+ def init_file
54
+ "#{project_root}/lib/lockdown/init.rb"
55
+ end
56
+
57
+ def controller_parent
58
+ ActionController::Base
59
+ end
60
+
61
+ def view_helper
62
+ ActionView::Base
63
+ end
64
+
65
+ def controller_class_name(str)
66
+ str = "#{str}Controller"
67
+ if str.include?("__")
68
+ str.split("__").collect{|p| Lockdown.camelize(p)}.join("::")
69
+ else
70
+ Lockdown.camelize(str)
71
+ end
72
+ end
73
+ end
74
+
75
+ module System
76
+ include Lockdown::Frameworks::Rails::Controller
77
+
78
+ def skip_sync?
79
+ Lockdown::System.fetch(:skip_db_sync_in).include?(ENV['RAILS_ENV'])
80
+ end
81
+
82
+ def load_controller_classes
83
+ @controller_classes = {}
84
+
85
+ maybe_load_framework_controller_parent
86
+
87
+ ApplicationController.helper_method :authorized?
88
+
89
+ ApplicationController.before_filter do |c|
90
+ c.set_current_user
91
+ c.configure_lockdown
92
+ c.check_request_authorization
93
+ end
94
+
95
+ ApplicationController.filter_parameter_logging :password,
96
+ :password_confirmation
97
+
98
+ ApplicationController.rescue_from SecurityError,
99
+ :with => proc{|e| access_denied(e)}
100
+
101
+
102
+ Dir.chdir("#{Lockdown.project_root}/app/controllers") do
103
+ Dir["**/*.rb"].sort.each do |c|
104
+ next if c == "application.rb"
105
+ lockdown_load(c)
106
+ end
107
+ end
108
+
109
+ if ENV['RAILS_ENV'] != 'production'
110
+ if ActiveSupport.const_defined?("Dependencies")
111
+ ActiveSupport::Dependencies.clear
112
+ else
113
+ Dependencies.clear
114
+ end
115
+ end
116
+ end
117
+
118
+ def maybe_load_framework_controller_parent
119
+ if ::Rails::VERSION::MAJOR >= 2 && ::Rails::VERSION::MINOR >= 3
120
+ filename = "application_controller.rb"
121
+ else
122
+ filename = "application.rb"
123
+ end
124
+
125
+ require_or_load(filename)
126
+ end
127
+
128
+ def lockdown_load(filename)
129
+ klass = Lockdown.class_name_from_file(filename)
130
+
131
+ require_or_load(filename)
132
+
133
+ @controller_classes[klass] = Lockdown.qualified_const_get(klass)
134
+ end
135
+
136
+ def require_or_load(filename)
137
+ if ActiveSupport.const_defined?("Dependencies")
138
+ ActiveSupport::Dependencies.require_or_load(filename)
139
+ else
140
+ Dependencies.require_or_load(filename)
141
+ end
142
+ end
143
+ end # System
144
+ end # Rails
145
+ end # Frameworks
146
+ end # Lockdown
@@ -0,0 +1,147 @@
1
+ module Lockdown
2
+ module Frameworks
3
+ module Rails
4
+ module Controller
5
+
6
+ def available_actions(klass)
7
+ if klass.respond_to?(:action_methods)
8
+ klass.action_methods
9
+ else
10
+ klass.public_instance_methods - klass.hidden_actions
11
+ end
12
+ end
13
+
14
+ def controller_name(klass)
15
+ klass.controller_name
16
+ end
17
+
18
+ # Locking methods
19
+ module Lock
20
+ def configure_lockdown
21
+ check_session_expiry
22
+ store_location
23
+ end
24
+
25
+ def set_current_user
26
+ login_from_basic_auth? unless logged_in?
27
+ if logged_in?
28
+ Thread.current[:who_did_it] = Lockdown::System.
29
+ call(self, :who_did_it)
30
+ end
31
+ end
32
+
33
+ def check_request_authorization
34
+ unless authorized?(path_from_hash(params))
35
+ raise SecurityError, "Authorization failed for params #{params.inspect}"
36
+ end
37
+ end
38
+
39
+ def path_allowed?(url)
40
+ session[:access_rights] ||= Lockdown::System.public_access
41
+ session[:access_rights].include?(url)
42
+ end
43
+
44
+ def check_session_expiry
45
+ if session[:expiry_time] && session[:expiry_time] < Time.now
46
+ nil_lockdown_values
47
+ Lockdown::System.call(self, :session_timeout_method)
48
+ end
49
+ session[:expiry_time] = Time.now + Lockdown::System.fetch(:session_timeout)
50
+ end
51
+
52
+ def store_location
53
+ if (request.method == :get) && (session[:thispage] != sent_from_uri)
54
+ session[:prevpage] = session[:thispage] || ''
55
+ session[:thispage] = sent_from_uri
56
+ end
57
+ end
58
+
59
+ def sent_from_uri
60
+ request.request_uri
61
+ end
62
+
63
+ def authorized?(url, method = nil)
64
+ return false unless url
65
+
66
+ return true if current_user_is_admin?
67
+
68
+ method ||= (params[:method] || request.method)
69
+
70
+ url_parts = URI::split(url.strip)
71
+
72
+ url = url_parts[5]
73
+
74
+ return true if path_allowed?(url)
75
+
76
+ begin
77
+ hash = ActionController::Routing::Routes.recognize_path(url, :method => method)
78
+ return path_allowed?(path_from_hash(hash)) if hash
79
+ rescue Exception
80
+ # continue on
81
+ end
82
+
83
+ # Passing in different domain
84
+ return remote_url?(url_parts[2])
85
+ end
86
+
87
+ def access_denied(e)
88
+
89
+ RAILS_DEFAULT_LOGGER.info "Access denied: #{e}"
90
+
91
+ if Lockdown::System.fetch(:logout_on_access_violation)
92
+ reset_session
93
+ end
94
+ respond_to do |format|
95
+ format.html do
96
+ store_location
97
+ redirect_to Lockdown::System.fetch(:access_denied_path)
98
+ return
99
+ end
100
+ format.xml do
101
+ headers["Status"] = "Unauthorized"
102
+ headers["WWW-Authenticate"] = %(Basic realm="Web Password")
103
+ render :text => e.message, :status => "401 Unauthorized"
104
+ return
105
+ end
106
+ end
107
+ end
108
+
109
+ def path_from_hash(hash)
110
+ hash[:controller].to_s + "/" + hash[:action].to_s
111
+ end
112
+
113
+ def remote_url?(domain = nil)
114
+ return false if domain.nil? || domain.strip.length == 0
115
+ request.host.downcase != domain.downcase
116
+ end
117
+
118
+ def redirect_back_or_default(default)
119
+ if session[:prevpage].nil? || session[:prevpage].blank?
120
+ redirect_to(default)
121
+ else
122
+ redirect_to(session[:prevpage])
123
+ end
124
+ end
125
+
126
+ # Called from current_user. Now, attempt to login by
127
+ # basic authentication information.
128
+ def login_from_basic_auth?
129
+ username, passwd = get_auth_data
130
+ if username && passwd
131
+ set_session_user ::User.authenticate(username, passwd)
132
+ end
133
+ end
134
+
135
+ @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
136
+ # gets BASIC auth info
137
+ def get_auth_data
138
+ auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
139
+ auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
140
+ return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
141
+ end
142
+ end # Lock
143
+ end # Controller
144
+ end # Rails
145
+ end # Frameworks
146
+ end # Lockdown
147
+
@@ -0,0 +1,61 @@
1
+ module Lockdown
2
+ module Frameworks
3
+ module Rails
4
+ module View
5
+ def self.included(base)
6
+ base.class_eval do
7
+ alias_method :link_to_open, :link_to
8
+ alias_method :link_to, :link_to_secured
9
+
10
+ alias_method :button_to_open, :button_to
11
+ alias_method :button_to, :button_to_secured
12
+ end
13
+ end
14
+
15
+ def link_to_secured(name, options = {}, html_options = nil)
16
+ url = url_for(options)
17
+
18
+ method = html_options ? html_options[:method] : nil
19
+
20
+ url_to_authorize = remove_subdirectory(url)
21
+
22
+ if authorized?(url_to_authorize, method)
23
+ return link_to_open(name, url, html_options)
24
+ end
25
+ return ""
26
+ end
27
+
28
+ def button_to_secured(name, options = {}, html_options = nil)
29
+ url = url_for(options)
30
+
31
+ method = html_options ? html_options[:method] : nil
32
+
33
+ url_to_authorize = remove_subdirectory(url)
34
+
35
+ if authorized?(url_to_authorize, method)
36
+ return button_to_open(name, url, html_options)
37
+ end
38
+ return ""
39
+ end
40
+
41
+ def link_to_or_show(name, options = {}, html_options = nil)
42
+ lnk = link_to(name, options, html_options)
43
+ lnk.length == 0 ? name : lnk
44
+ end
45
+
46
+ def links(*lis)
47
+ rvalue = []
48
+ lis.each{|link| rvalue << link if link.length > 0 }
49
+ rvalue.join( Lockdown::System.fetch(:link_separator) )
50
+ end
51
+
52
+
53
+ def remove_subdirectory(url)
54
+ subdir = Lockdown::System.fetch(:subdirectory)
55
+ subdir ? url.gsub(/^\/?#{subdir}/,'') : url
56
+ end
57
+
58
+ end # View
59
+ end # Rails
60
+ end # Frameworks
61
+ end # Lockdown
@@ -0,0 +1,95 @@
1
+ module Lockdown
2
+ module Helper
3
+ def class_name_from_file(str)
4
+ str.split(".")[0].split("/").collect{|s| camelize(s) }.join("::")
5
+ end
6
+
7
+ # If str_sym is a Symbol (:users), return "Users"
8
+ # If str_sym is a String ("Users"), return :users
9
+ def convert_reference_name(str_sym)
10
+ if str_sym.is_a?(Symbol)
11
+ titleize(str_sym)
12
+ else
13
+ underscore(str_sym).tr(' ','_').to_sym
14
+ end
15
+ end
16
+
17
+ def get_string(value)
18
+ if value.respond_to?(:name)
19
+ string_name(value.name)
20
+ else
21
+ string_name(value)
22
+ end
23
+ end
24
+
25
+ def get_symbol(value)
26
+ if value.respond_to?(:name)
27
+ symbol_name(value.name)
28
+ elsif value.is_a?(String)
29
+ symbol_name(value)
30
+ else
31
+ value
32
+ end
33
+ end
34
+
35
+ def camelize(str)
36
+ str.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
37
+ end
38
+
39
+ def random_string(len = 10)
40
+ chars = ("a".."z").to_a + ("A".."Z").to_a + ("0".."9").to_a
41
+ Array.new(len){||chars[rand(chars.size)]}.join
42
+ end
43
+
44
+ def administrator_group_string
45
+ string_name(administrator_group_symbol)
46
+ end
47
+
48
+ def administrator_group_symbol
49
+ :administrators
50
+ end
51
+
52
+ def qualified_const_defined?(klass)
53
+ if klass =~ /::/
54
+ namespace, klass = klass.split("::")
55
+ eval("#{namespace}.const_defined?(#{klass})") if const_defined?(namespace)
56
+ else
57
+ const_defined?(klass)
58
+ end
59
+ end
60
+
61
+ def qualified_const_get(klass)
62
+ if klass =~ /::/
63
+ namespace, klass = klass.split("::")
64
+ eval(namespace).const_get(klass)
65
+ else
66
+ const_get(klass)
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ def string_name(str_sym)
73
+ str_sym.is_a?(Symbol) ? convert_reference_name(str_sym) : str_sym
74
+ end
75
+
76
+ def symbol_name(str_sym)
77
+ str_sym.is_a?(String) ? convert_reference_name(str_sym) : str_sym
78
+ end
79
+
80
+ def titleize(str)
81
+ humanize(underscore(str)).gsub(/\b([a-z])/) { $1.capitalize }
82
+ end
83
+
84
+ def humanize(str)
85
+ str.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize
86
+ end
87
+
88
+ def underscore(str)
89
+ str.to_s.gsub(/::/, '/').
90
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
91
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
92
+ tr("-", "_").downcase
93
+ end
94
+ end
95
+ end