revo-lockdown 0.9.6 → 1.6.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.DS_Store
2
+ *.swp
3
+ pkg/**
4
+ doc/**
5
+ email.txt
6
+ coverage/**
data/Rakefile CHANGED
@@ -1,17 +1,9 @@
1
- # Look in the tasks/setup.rb file for the various options that can be
2
- # configured in this Rakefile. The .rake files in the tasks directory
3
- # are where the options are used.
4
-
5
- begin
6
- require 'bones'
7
- Bones.setup
8
- rescue LoadError
9
- load 'tasks/setup.rb'
10
- end
11
-
12
- ensure_in_path 'lib'
13
- require 'lockdown'
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rcov'
4
+ require 'spec/rake/spectask'
14
5
 
6
+ require 'lib/lockdown.rb'
15
7
  task :default => 'rcov'
16
8
 
17
9
  desc "Flog your code for Justice!"
@@ -27,15 +19,20 @@ Spec::Rake::SpecTask.new(:rcov) do |t|
27
19
  t.rcov_opts = IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
28
20
  end
29
21
 
30
- PROJ.name = 'lockdown'
31
- PROJ.authors = 'Andrew Stone'
32
- PROJ.email = 'andy@stonean.com'
33
- PROJ.url = 'http://stonean.com/wiki/lockdown'
34
- PROJ.version = Lockdown::VERSION
35
- PROJ.rubyforge.name = 'lockdown'
36
-
37
- PROJ.spec.opts << '--color'
38
- PROJ.exclude << ".swp"
39
- PROJ.exclude << ".gitignore"
40
-
41
- # EOF
22
+ begin
23
+ require 'jeweler'
24
+ Jeweler::Tasks.new do |gemspec|
25
+ gemspec.name = "revo-lockdown"
26
+ gemspec.version = Lockdown.version
27
+ gemspec.rubyforge_project = "lockdown"
28
+ gemspec.summary = "Authorization system for Rails 2.x"
29
+ gemspec.description = "Restrict access to your controller actions. Supports basic model level restrictions as well"
30
+ gemspec.email = "andy@stonean.com"
31
+ gemspec.homepage = "http://stonean.com/wiki/lockdown"
32
+ gemspec.authors = ["Andrew Stone", "Revo Pty. Ltd."]
33
+ gemspec.add_development_dependency('rspec')
34
+ end
35
+ Jeweler::GemcutterTasks.new
36
+ rescue LoadError
37
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
38
+ end
@@ -10,13 +10,17 @@ module Lockdown
10
10
  @permissions = Lockdown::System.get_permissions
11
11
  @user_groups = Lockdown::System.get_user_groups
12
12
 
13
+ unless ::Permission.table_exists? && Lockdown.user_group_class.table_exists?
14
+ Lockdown.logger.info ">> Lockdown tables not found. Skipping database sync."
15
+ return
16
+ end
13
17
  create_new_permissions
14
18
 
15
19
  delete_extinct_permissions
16
20
 
17
21
  maintain_user_groups
18
22
  rescue Exception => e
19
- puts ">> Lockdown sync failed: #{e}"
23
+ Lockdown.logger.error ">> Lockdown sync failed: #{e.backtrace.join("\n")}"
20
24
  end
21
25
 
22
26
  # Create permissions not found in the database
@@ -26,7 +30,7 @@ module Lockdown
26
30
  str = Lockdown.get_string(key)
27
31
  p = ::Permission.find(:first, :conditions => ["name = ?", str])
28
32
  unless p
29
- puts ">> Lockdown: Permission not found in db: #{str}, creating."
33
+ Lockdown.logger.info ">> Lockdown: Permission not found in db: #{str}, creating."
30
34
  ::Permission.create(:name => str)
31
35
  end
32
36
  end
@@ -37,8 +41,14 @@ module Lockdown
37
41
  db_perms = ::Permission.find(:all).dup
38
42
  db_perms.each do |dbp|
39
43
  unless @permissions.include?(Lockdown.get_symbol(dbp.name))
40
- puts ">> Lockdown: Permission no longer in init.rb: #{dbp.name}, deleting."
41
- Lockdown.database_execute("delete from permissions_user_groups where permission_id = #{dbp.id}")
44
+ Lockdown.logger.info ">> Lockdown: Permission no longer in init.rb: #{dbp.name}, deleting."
45
+ ug_table = Lockdown.user_groups_hbtm_reference.to_s
46
+ if "permissions" < ug_table
47
+ join_table = "permissions_#{ug_table}"
48
+ else
49
+ join_table = "#{ug_table}_permissions"
50
+ end
51
+ Lockdown.database_execute("delete from #{join_table} where permission_id = #{dbp.id}")
42
52
  dbp.destroy
43
53
  end
44
54
  end
@@ -48,7 +58,7 @@ module Lockdown
48
58
  # Create user groups not found in the database
49
59
  @user_groups.each do |key|
50
60
  str = Lockdown.get_string(key)
51
- unless ug = ::UserGroup.find(:first, :conditions => ["name = ?", str])
61
+ unless ug = Lockdown.user_group_class.find(:first, :conditions => ["name = ?", str])
52
62
  create_user_group(str, key)
53
63
  else
54
64
  # Remove permissions from user group not found in init.rb
@@ -61,14 +71,26 @@ module Lockdown
61
71
  end
62
72
 
63
73
  def create_user_group(name_str, key)
64
- puts ">> Lockdown: UserGroup not in the db: #{name_str}, creating."
65
- ug = ::UserGroup.create(:name => name_str)
74
+ Lockdown.logger.info ">> Lockdown: #{Lockdown::System.fetch(:user_group_model)} not in the db: #{name_str}, creating."
75
+ ug = Lockdown.user_group_class.create(:name => name_str)
66
76
  #Inefficient, definitely, but shouldn't have any issues across orms.
77
+ #
67
78
  Lockdown::System.permissions_for_user_group(key).each do |perm|
68
- p = ::Permission.find(:first, :conditions => ["name = ?",
69
- Lockdown.get_string(perm)])
70
79
 
71
- Lockdown.database_execute "insert into permissions_user_groups(permission_id, user_group_id) values(#{p.id}, #{ug.id})"
80
+ if Lockdown::System.permission_assigned_automatically?(perm)
81
+ Lockdown.logger.info ">> Permission #{perm} cannot be assigned to #{name_str}. Already belongs to built in user group (public or protected)."
82
+ raise InvalidPermissionAssignment, "Invalid permission assignment"
83
+ end
84
+
85
+ p = ::Permission.find(:first, :conditions => ["name = ?", Lockdown.get_string(perm)])
86
+
87
+ ug_table = Lockdown.user_groups_hbtm_reference.to_s
88
+ if "permissions" < ug_table
89
+ join_table = "permissions_#{ug_table}"
90
+ else
91
+ join_table = "#{ug_table}_permissions"
92
+ end
93
+ Lockdown.database_execute "insert into #{join_table}(permission_id, #{Lockdown.user_group_id_reference}) values(#{p.id}, #{ug.id})"
72
94
  end
73
95
  end
74
96
 
@@ -77,7 +99,7 @@ module Lockdown
77
99
  perm_sym = Lockdown.get_symbol(perm)
78
100
  perm_string = Lockdown.get_string(perm)
79
101
  unless Lockdown::System.permissions_for_user_group(key).include?(perm_sym)
80
- puts ">> Lockdown: Permission: #{perm_string} no longer associated to User Group: #{ug.name}, deleting."
102
+ Lockdown.logger.info ">> Lockdown: Permission: #{perm_string} no longer associated to User Group: #{ug.name}, deleting."
81
103
  ug.permissions.delete(perm)
82
104
  end
83
105
  end
@@ -93,7 +115,7 @@ module Lockdown
93
115
  end
94
116
  # if not found, add it
95
117
  unless found
96
- puts ">> Lockdown: Permission: #{perm_string} not found for User Group: #{ug.name}, adding it."
118
+ Lockdown.logger.info ">> Lockdown: Permission: #{perm_string} not found for User Group: #{ug.name}, adding it."
97
119
  p = ::Permission.find(:first, :conditions => ["name = ?", perm_string])
98
120
  ug.permissions << p
99
121
  end
@@ -0,0 +1,11 @@
1
+ module Lockdown
2
+ class InvalidRuleAssignment < StandardError; end
3
+
4
+ class InvalidRuleContext < StandardError; end
5
+
6
+ class PermissionScopeCollision < StandardError; end
7
+
8
+ class InvalidPermissionAssignment < StandardError; end
9
+
10
+ class GroupUndefinedError < StandardError; end
11
+ end
@@ -4,11 +4,7 @@ module Lockdown
4
4
  module Controller
5
5
 
6
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
7
+ klass.action_methods
12
8
  end
13
9
 
14
10
  def controller_name(klass)
@@ -17,13 +13,17 @@ module Lockdown
17
13
 
18
14
  # Locking methods
19
15
  module Lock
16
+
20
17
  def configure_lockdown
18
+ Lockdown.maybe_parse_init
21
19
  check_session_expiry
22
20
  store_location
23
21
  end
24
22
 
23
+ # Basic auth functionality needs to be reworked as
24
+ # Lockdown doesn't provide authentication functionality.
25
25
  def set_current_user
26
- login_from_basic_auth? unless logged_in?
26
+ #login_from_basic_auth? unless logged_in?
27
27
  if logged_in?
28
28
  Thread.current[:who_did_it] = Lockdown::System.
29
29
  call(self, :who_did_it)
@@ -32,9 +32,11 @@ module Lockdown
32
32
 
33
33
  def check_request_authorization
34
34
  unless authorized?(path_from_hash(params))
35
- raise SecurityError, "Authorization failed for params #{params.inspect}"
35
+ raise SecurityError, "Authorization failed! \nparams: #{params.inspect}\nsession: #{session.inspect}"
36
36
  end
37
37
  end
38
+
39
+ protected
38
40
 
39
41
  def path_allowed?(url)
40
42
  session[:access_rights] ||= Lockdown::System.public_access
@@ -61,6 +63,9 @@ module Lockdown
61
63
  end
62
64
 
63
65
  def authorized?(url, method = nil)
66
+ # Reset access unless caching?
67
+ add_lockdown_session_values unless Lockdown.caching?
68
+
64
69
  return false unless url
65
70
 
66
71
  return true if current_user_is_admin?
@@ -69,24 +74,31 @@ module Lockdown
69
74
 
70
75
  url_parts = URI::split(url.strip)
71
76
 
72
- url = url_parts[5]
77
+ path = url_parts[5]
73
78
 
74
- return true if path_allowed?(url)
79
+ return true if path_allowed?(path)
75
80
 
76
81
  begin
77
- hash = ActionController::Routing::Routes.recognize_path(url, :method => method)
82
+ hash = ActionController::Routing::Routes.recognize_path(path, :method => method)
78
83
  return path_allowed?(path_from_hash(hash)) if hash
79
- rescue Exception
84
+ rescue Exception => e
80
85
  # continue on
81
86
  end
82
87
 
88
+ # Mailto link
89
+ return true if url =~ /^mailto:/
90
+
91
+ # Public file
92
+ file = File.join(RAILS_ROOT, 'public', url)
93
+ return true if File.exists?(file)
94
+
83
95
  # Passing in different domain
84
96
  return remote_url?(url_parts[2])
85
97
  end
86
98
 
87
- def access_denied(e)
99
+ def ld_access_denied(e)
88
100
 
89
- RAILS_DEFAULT_LOGGER.info "Access denied: #{e}"
101
+ Lockdown.logger.info "Access denied: #{e}"
90
102
 
91
103
  if Lockdown::System.fetch(:logout_on_access_violation)
92
104
  reset_session
@@ -15,7 +15,7 @@ module Lockdown
15
15
  def link_to_secured(name, options = {}, html_options = nil)
16
16
  url = url_for(options)
17
17
 
18
- method = html_options ? html_options[:method] : nil
18
+ method = html_options ? html_options[:method] : :get
19
19
 
20
20
  url_to_authorize = remove_subdirectory(url)
21
21
 
@@ -28,7 +28,7 @@ module Lockdown
28
28
  def button_to_secured(name, options = {}, html_options = nil)
29
29
  url = url_for(options)
30
30
 
31
- method = html_options ? html_options[:method] : nil
31
+ method = html_options ? html_options[:method] : :get
32
32
 
33
33
  url_to_authorize = remove_subdirectory(url)
34
34
 
@@ -13,34 +13,39 @@ module Lockdown
13
13
  mod.extend Lockdown::Frameworks::Rails::Environment
14
14
  mixin
15
15
  end
16
-
16
+
17
17
  def mixin
18
- Lockdown.controller_parent.class_eval do
18
+ mixin_controller
19
+
20
+ Lockdown.view_helper.class_eval do
21
+ include Lockdown::Frameworks::Rails::View
22
+ end
23
+
24
+ Lockdown.system.class_eval do
25
+ extend Lockdown::Frameworks::Rails::System
26
+ end
27
+ end
28
+
29
+ def mixin_controller(klass = Lockdown.controller_parent)
30
+ klass.class_eval do
19
31
  include Lockdown::Session
20
32
  include Lockdown::Frameworks::Rails::Controller::Lock
21
33
  end
22
34
 
23
- Lockdown.controller_parent.helper_method :authorized?
35
+ klass.helper_method :authorized?
36
+
37
+ klass.hide_action(:set_current_user, :configure_lockdown, :check_request_authorization, :check_model_authorization)
24
38
 
25
- Lockdown.controller_parent.before_filter do |c|
39
+ klass.before_filter do |c|
26
40
  c.set_current_user
27
41
  c.configure_lockdown
28
42
  c.check_request_authorization
43
+ c.check_model_authorization
29
44
  end
30
45
 
31
- Lockdown.controller_parent.filter_parameter_logging :password,
32
- :password_confirmation
46
+ klass.filter_parameter_logging :password, :password_confirmation
33
47
 
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
48
+ klass.rescue_from SecurityError, :with => proc{|e| ld_access_denied(e)}
44
49
  end
45
50
  end # class block
46
51
 
@@ -54,12 +59,28 @@ module Lockdown
54
59
  "#{project_root}/lib/lockdown/init.rb"
55
60
  end
56
61
 
62
+ def view_helper
63
+ ::ActionView::Base
64
+ end
65
+
66
+ # cache_classes is true in production and testing, need to
67
+ # modify the ApplicationController
57
68
  def controller_parent
58
- ActionController::Base
69
+ if caching?
70
+ ApplicationController
71
+ else
72
+ ActionController::Base
73
+ end
74
+ end
75
+
76
+ def caching?
77
+ ::Rails.configuration.cache_classes
59
78
  end
60
79
 
61
- def view_helper
62
- ActionView::Base
80
+ # cache_classes is true in production and testing, need to
81
+ # do an instance eval instead
82
+ def add_controller_method(code)
83
+ Lockdown.controller_parent.class_eval code, __FILE__,__LINE__ +1
63
84
  end
64
85
 
65
86
  def controller_class_name(str)
@@ -70,75 +91,21 @@ module Lockdown
70
91
  Lockdown.camelize(str)
71
92
  end
72
93
  end
94
+
95
+ def fetch_controller_class(str)
96
+ eval("::#{controller_class_name(str)}")
97
+ end
73
98
  end
74
99
 
75
100
  module System
76
101
  include Lockdown::Frameworks::Rails::Controller
77
102
 
78
103
  def skip_sync?
79
- Lockdown::System.fetch(:skip_db_sync_in).include?(ENV['RAILS_ENV'])
104
+ Lockdown.system.fetch(:skip_db_sync_in).include?(framework_environment)
80
105
  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
106
+
107
+ def framework_environment
108
+ ::Rails.env
142
109
  end
143
110
  end # System
144
111
  end # Rails
@@ -1,3 +1,5 @@
1
+ require 'active_support'
2
+
1
3
  module Lockdown
2
4
  module Helper
3
5
  def class_name_from_file(str)
@@ -10,10 +12,42 @@ module Lockdown
10
12
  if str_sym.is_a?(Symbol)
11
13
  titleize(str_sym)
12
14
  else
13
- underscore(str_sym).tr(' ','_').to_sym
15
+ str_sym.underscore.tr(' ','_').to_sym
14
16
  end
15
17
  end
16
18
 
19
+ def user_group_class
20
+ eval(user_group_model_string)
21
+ end
22
+
23
+ def user_groups_hbtm_reference
24
+ user_group_model_string.underscore.pluralize.to_sym
25
+ end
26
+
27
+ def user_group_id_reference
28
+ user_group_model_string.underscore + "_id"
29
+ end
30
+
31
+ def user_class
32
+ eval(user_model_string)
33
+ end
34
+
35
+ def users_hbtm_reference
36
+ user_model_string.underscore.pluralize.to_sym
37
+ end
38
+
39
+ def user_id_reference
40
+ user_model_string.underscore + "_id"
41
+ end
42
+
43
+ def user_group_model_string
44
+ Lockdown.system.fetch(:user_group_model) || "UserGroup"
45
+ end
46
+
47
+ def user_model_string
48
+ Lockdown.system.fetch(:user_model) || "User"
49
+ end
50
+
17
51
  def get_string(value)
18
52
  if value.respond_to?(:name)
19
53
  string_name(value.name)
@@ -49,24 +83,6 @@ module Lockdown
49
83
  :administrators
50
84
  end
51
85
 
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
86
  private
71
87
 
72
88
  def string_name(str_sym)