lockdown 0.7.1 → 0.8.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.
Files changed (46) hide show
  1. data/.DS_Store +0 -0
  2. data/History.txt +3 -0
  3. data/README.txt +1 -1
  4. data/Rakefile +16 -1
  5. data/lib/lockdown/context.rb +41 -0
  6. data/lib/lockdown/database.rb +11 -14
  7. data/lib/lockdown/frameworks/rails/controller.rb +57 -4
  8. data/lib/lockdown/frameworks/rails/view.rb +1 -1
  9. data/lib/lockdown/frameworks/rails.rb +21 -10
  10. data/lib/lockdown/helper.rb +1 -1
  11. data/lib/lockdown/permission.rb +204 -0
  12. data/lib/lockdown/rules.rb +287 -0
  13. data/lib/lockdown/session.rb +8 -6
  14. data/lib/lockdown/system.rb +35 -88
  15. data/lib/lockdown.rb +52 -49
  16. data/rails_generators/.DS_Store +0 -0
  17. data/rails_generators/lockdown/.DS_Store +0 -0
  18. data/rails_generators/lockdown/lockdown_generator.rb +5 -5
  19. data/rails_generators/lockdown/templates/.DS_Store +0 -0
  20. data/rails_generators/lockdown/templates/lib/.DS_Store +0 -0
  21. data/rails_generators/lockdown/templates/lib/lockdown/init.rb +27 -19
  22. data/rails_generators/lockdown/templates/lib/lockdown/session.rb +1 -3
  23. data/spec/lockdown/database_spec.rb +158 -0
  24. data/spec/lockdown/frameworks/rails/controller_spec.rb +220 -0
  25. data/spec/lockdown/frameworks/rails/view_spec.rb +87 -0
  26. data/spec/lockdown/frameworks/rails_spec.rb +170 -0
  27. data/spec/lockdown/permission_spec.rb +156 -0
  28. data/spec/lockdown/rules_spec.rb +109 -0
  29. data/spec/lockdown/session_spec.rb +88 -0
  30. data/spec/lockdown/system_spec.rb +59 -0
  31. data/spec/lockdown_spec.rb +19 -0
  32. data/spec/rcov.opts +5 -0
  33. data/spec/spec.opts +3 -0
  34. data/spec/spec_helper.rb +1 -0
  35. data/tasks/post_load.rake +2 -7
  36. data/tasks/setup.rb +24 -3
  37. metadata +23 -12
  38. data/.gitignore +0 -5
  39. data/Manifest.txt +0 -51
  40. data/lib/lockdown/controller.rb +0 -64
  41. data/lib/lockdown/frameworks/merb/controller.rb +0 -63
  42. data/lib/lockdown/frameworks/merb/view.rb +0 -32
  43. data/lib/lockdown/frameworks/merb.rb +0 -84
  44. data/lib/lockdown/orms/data_mapper.rb +0 -70
  45. data/lib/lockdown/rights.rb +0 -208
  46. data/tasks/manifest.rake +0 -48
data/.DS_Store ADDED
Binary file
data/History.txt CHANGED
@@ -1,3 +1,6 @@
1
+ == 0.7.1 2009-01-xx
2
+ * Update init.rb with documentation on how to use admin namespaces
3
+
1
4
  == 0.7.0 2009-01-xx
2
5
  * Removed lockdown as an executable. Will always go through the generator used by the framework.
3
6
  * Removed references to classy inheritance. Directly coded some of classy inheritance's functionality into User model.
data/README.txt CHANGED
@@ -8,7 +8,7 @@ Lockdown is a authentication/authorization system for RubyOnRails (ver >= 2.1).
8
8
 
9
9
  == INSTALL:
10
10
 
11
- sudo gem install lockdown
11
+ sudo gem install lockdown
12
12
 
13
13
  == LICENSE:
14
14
 
data/Rakefile CHANGED
@@ -12,7 +12,20 @@ end
12
12
  ensure_in_path 'lib'
13
13
  require 'lockdown'
14
14
 
15
- task :default => 'spec:run'
15
+ task :default => 'rcov'
16
+
17
+ desc "Flog your code for Justice!"
18
+ task :flog do
19
+ sh('flog lib/**/*.rb')
20
+ end
21
+
22
+ desc "Run all specs and rcov in a non-sucky way"
23
+ Spec::Rake::SpecTask.new(:rcov) do |t|
24
+ t.spec_opts = IO.readlines("spec/spec.opts").map {|l| l.chomp.split " "}.flatten
25
+ t.spec_files = FileList['spec/**/*_spec.rb']
26
+ t.rcov = true
27
+ t.rcov_opts = IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
28
+ end
16
29
 
17
30
  PROJ.name = 'lockdown'
18
31
  PROJ.authors = 'Andrew Stone'
@@ -22,5 +35,7 @@ PROJ.version = Lockdown::VERSION
22
35
  PROJ.rubyforge.name = 'lockdown'
23
36
 
24
37
  PROJ.spec.opts << '--color'
38
+ PROJ.exclude << ".swp"
39
+ PROJ.exclude << ".gitignore"
25
40
 
26
41
  # EOF
@@ -0,0 +1,41 @@
1
+ module Lockdown
2
+ class Context
3
+ attr_accessor :name, :allowed_methods
4
+
5
+ def to_s
6
+ self.class.to_s
7
+ end
8
+
9
+ def allows?(method_name)
10
+ @allowed_methods.include?(method_name)
11
+ end
12
+ end
13
+
14
+ class RootContext < Context
15
+ def initialize(name)
16
+ @name = name
17
+ @allowed_methods = %w(with_controller and_controller to_model)
18
+ end
19
+ end
20
+
21
+ class ControllerContext < Context
22
+ def initialize(name)
23
+ @name = name
24
+ @allowed_methods = %w(with_controller and_controller to_model only_methods except_methods)
25
+ end
26
+ end
27
+
28
+ class ModelContext < Context
29
+ def initialize(name)
30
+ @name = name
31
+ @allowed_methods = %w(where)
32
+ end
33
+ end
34
+
35
+ class ModelWhereContext < Context
36
+ def initialize(name)
37
+ @name = name
38
+ @allowed_methods = %w(is_in includes equals)
39
+ end
40
+ end
41
+ end
@@ -19,24 +19,22 @@ module Lockdown
19
19
  puts ">> Lockdown sync failed: #{e}"
20
20
  end
21
21
 
22
- private
23
-
24
22
  # Create permissions not found in the database
25
23
  def create_new_permissions
26
24
  @permissions.each do |key|
27
25
  next if Lockdown::System.permission_assigned_automatically?(key)
28
26
  str = Lockdown.get_string(key)
29
- p = Permission.find(:first, :conditions => ["name = ?", str])
27
+ p = ::Permission.find(:first, :conditions => ["name = ?", str])
30
28
  unless p
31
29
  puts ">> Lockdown: Permission not found in db: #{str}, creating."
32
- Permission.create(:name => str)
30
+ ::Permission.create(:name => str)
33
31
  end
34
32
  end
35
33
  end
36
34
 
37
35
  # Delete the permissions not found in init.rb
38
36
  def delete_extinct_permissions
39
- db_perms = Permission.find(:all).dup
37
+ db_perms = ::Permission.find(:all).dup
40
38
  db_perms.each do |dbp|
41
39
  unless @permissions.include?(Lockdown.get_symbol(dbp.name))
42
40
  puts ">> Lockdown: Permission no longer in init.rb: #{dbp.name}, deleting."
@@ -50,7 +48,7 @@ module Lockdown
50
48
  # Create user groups not found in the database
51
49
  @user_groups.each do |key|
52
50
  str = Lockdown.get_string(key)
53
- unless ug = UserGroup.find(:first, :conditions => ["name = ?", str])
51
+ unless ug = ::UserGroup.find(:first, :conditions => ["name = ?", str])
54
52
  create_user_group(str, key)
55
53
  else
56
54
  # Remove permissions from user group not found in init.rb
@@ -64,14 +62,13 @@ module Lockdown
64
62
 
65
63
  def create_user_group(name_str, key)
66
64
  puts ">> Lockdown: UserGroup not in the db: #{name_str}, creating."
67
- ug = UserGroup.create(:name => name_str)
65
+ ug = ::UserGroup.create(:name => name_str)
68
66
  #Inefficient, definitely, but shouldn't have any issues across orms.
69
- Lockdown::System.permissions_for_user_group(key) do |perm|
70
- p = Permission.find(:first, :conditions => ["name = ?", Lockdown.get_string(perm)])
71
- Lockdown.database_execute <<-SQL
72
- insert into permissions_user_groups(permission_id, user_group_id)
73
- values(#{p.id}, #{ug.id})
74
- SQL
67
+ Lockdown::System.permissions_for_user_group(key).each do |perm|
68
+ p = ::Permission.find(:first, :conditions => ["name = ?",
69
+ Lockdown.get_string(perm)])
70
+
71
+ Lockdown.database_execute "insert into permissions_user_groups(permission_id, user_group_id) values(#{p.id}, #{ug.id})"
75
72
  end
76
73
  end
77
74
 
@@ -97,7 +94,7 @@ module Lockdown
97
94
  # if not found, add it
98
95
  unless found
99
96
  puts ">> Lockdown: Permission: #{perm_string} not found for User Group: #{ug.name}, adding it."
100
- p = Permission.find(:first, :conditions => ["name = ?", perm_string])
97
+ p = ::Permission.find(:first, :conditions => ["name = ?", perm_string])
101
98
  ug.permissions << p
102
99
  end
103
100
  end
@@ -37,9 +37,45 @@ module Lockdown
37
37
  end
38
38
 
39
39
  module InstanceMethods
40
- def self.included(base)
41
- base.class_eval do
42
- include Lockdown::Controller::Core
40
+
41
+ def configure_lockdown
42
+ check_session_expiry
43
+ store_location
44
+ end
45
+
46
+ def set_current_user
47
+ login_from_basic_auth? unless logged_in?
48
+ if logged_in?
49
+ Thread.current[:profile_id] = current_profile_id
50
+ end
51
+ end
52
+
53
+ def check_request_authorization
54
+ unless authorized?(path_from_hash(params))
55
+ raise SecurityError, "Authorization failed for params #{params.inspect}"
56
+ end
57
+ end
58
+
59
+ def path_allowed?(url)
60
+ session[:access_rights] ||= Lockdown::System.public_access
61
+ session[:access_rights].include?(url)
62
+ end
63
+
64
+ def check_session_expiry
65
+ if session[:expiry_time] && session[:expiry_time] < Time.now
66
+ nil_lockdown_values
67
+ timeout_method = Lockdown::System.fetch(:session_timeout_method)
68
+ if timeout_method.is_a?(Symbol) && self.respond_to?(timeout_method)
69
+ send(timeout_method)
70
+ end
71
+ end
72
+ session[:expiry_time] = Time.now + Lockdown::System.fetch(:session_timeout)
73
+ end
74
+
75
+ def store_location
76
+ if (request.method == :get) && (session[:thispage] != sent_from_uri)
77
+ session[:prevpage] = session[:thispage] || ''
78
+ session[:thispage] = sent_from_uri
43
79
  end
44
80
  end
45
81
 
@@ -105,7 +141,24 @@ module Lockdown
105
141
  def redirect_back_or_default(default)
106
142
  session[:prevpage] ? redirect_to(session[:prevpage]) : redirect_to(default)
107
143
  end
108
-
144
+
145
+ # Called from current_user. Now, attempt to login by
146
+ # basic authentication information.
147
+ def login_from_basic_auth?
148
+ username, passwd = get_auth_data
149
+ if username && passwd
150
+ set_session_user ::User.authenticate(username, passwd)
151
+ end
152
+ end
153
+
154
+ @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
155
+ # gets BASIC auth info
156
+ def get_auth_data
157
+ auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
158
+ auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
159
+ return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
160
+ end
161
+
109
162
  end # InstanceMethods
110
163
  end # Lock
111
164
  end # Controller
@@ -42,7 +42,7 @@ module Lockdown
42
42
  def links(*lis)
43
43
  rvalue = []
44
44
  lis.each{|link| rvalue << link if link.length > 0 }
45
- rvalue.join(" | ")
45
+ rvalue.join( Lockdown::System.fetch(:link_separator) )
46
46
  end
47
47
  end # View
48
48
  end # Rails
@@ -30,7 +30,11 @@ module Lockdown
30
30
  module Environment
31
31
 
32
32
  def project_root
33
- RAILS_ROOT
33
+ ::RAILS_ROOT
34
+ end
35
+
36
+ def init_file
37
+ "#{project_root}/lib/lockdown/init.rb"
34
38
  end
35
39
 
36
40
  def controller_parent
@@ -80,23 +84,30 @@ module Lockdown
80
84
  end
81
85
 
82
86
  def maybe_load_framework_controller_parent
83
- if ActiveSupport.const_defined?("Dependencies")
84
- ActiveSupport::Dependencies.require_or_load("application.rb")
87
+ if ::Rails::VERSION::MAJOR >= 2 && ::Rails::VERSION::MINOR >= 3
88
+ filename = "application_controller.rb"
85
89
  else
86
- Dependencies.require_or_load("application.rb")
90
+ filename = "application.rb"
87
91
  end
92
+
93
+ require_or_load(filename)
94
+ end
95
+
96
+ def lockdown_load(filename)
97
+ klass = Lockdown.class_name_from_file(filename)
98
+
99
+ require_or_load(filename)
100
+
101
+ @controller_classes[klass] = Lockdown.qualified_const_get(klass)
88
102
  end
89
103
 
90
- def lockdown_load(file)
91
- klass = Lockdown.class_name_from_file(file)
104
+ def require_or_load(filename)
92
105
  if ActiveSupport.const_defined?("Dependencies")
93
- ActiveSupport::Dependencies.require_or_load(file)
106
+ ActiveSupport::Dependencies.require_or_load(filename)
94
107
  else
95
- Dependencies.require_or_load(file)
108
+ Dependencies.require_or_load(filename)
96
109
  end
97
- @controller_classes[klass] = Lockdown.qualified_const_get(klass)
98
110
  end
99
-
100
111
  end # System
101
112
  end # Rails
102
113
  end # Frameworks
@@ -66,7 +66,7 @@ module Lockdown
66
66
  const_get(klass)
67
67
  end
68
68
  end
69
-
69
+
70
70
  private
71
71
 
72
72
  def string_name(str_sym)
@@ -0,0 +1,204 @@
1
+ module Lockdown
2
+ class InvalidRuleContext < StandardError; end
3
+ class PermissionScopeCollision < StandardError; end
4
+
5
+ class Controller
6
+ attr_accessor :name, :access_methods
7
+
8
+ def initialize(name)
9
+ @name = name
10
+ end
11
+ end
12
+
13
+ class Model
14
+ attr_accessor :name, :controller_method, :model_method, :association
15
+
16
+ def initialize(name)
17
+ @name = name
18
+ end
19
+
20
+ def association=(type)
21
+ @assocation = type
22
+ end
23
+ end
24
+
25
+ class Permission
26
+ attr_reader :name, :controllers, :models
27
+
28
+ # A Permission is a set of rules that are, through UserGroups, assigned
29
+ # to users to allow access to system resources.
30
+ #
31
+ # ==== Summary of controller oriented methods:
32
+ #
33
+ # # defines which controller we're talking about
34
+ # .with_controller(:controller_name) #all_methods is the default
35
+ #
36
+ # # only these methods on the controller
37
+ # .only_methods(:meth1, :meth2)
38
+ #
39
+ # # all controller methods except these
40
+ # .except_methods(:meth1, :meth2)
41
+ #
42
+ # ==== Summary of model oriented methods:
43
+ #
44
+ # # defines which model we're talking about
45
+ # .to_model(:model_name)
46
+ #
47
+ # # data_method must be available to the controller
48
+ # .where(:data_method)
49
+ #
50
+ # # model_name.value_method must equal data_method
51
+ # .equals(:value_method)
52
+ #
53
+ # # model_name.values_method.include?(data_method)
54
+ # .is_in(:values_method)
55
+ #
56
+ #
57
+ # ==== Example:
58
+ #
59
+ # # Define a permission called 'Manage Users' that allows users access
60
+ # # all methods on the users_controller
61
+ #
62
+ # set_permission(:manage_users).
63
+ # with_controller(:users)
64
+ #
65
+ # # Define a permission called "My Account" that only allows a user access
66
+ # # to methods show and update and the current_user_id must match the id
67
+ # # of the user being modified
68
+ #
69
+ # set_permission(:my_account).
70
+ # with_controller(:users).
71
+ # only_methods(:show, :update).
72
+ # to_model(:user).
73
+ # where(:current_user_id).
74
+ # equals(:id)
75
+ #
76
+ def initialize(name_symbol)
77
+ @name = name_symbol
78
+ @controllers = {}
79
+ @models = {}
80
+ @current_context = Lockdown::RootContext.new(name_symbol)
81
+ end
82
+
83
+ def with_controller(name_symbol)
84
+ validate_context
85
+
86
+ controller = Controller.new(name_symbol)
87
+ controller.access_methods = paths_for(name_symbol)
88
+ @controllers[name_symbol] = controller
89
+ @current_context = Lockdown::ControllerContext.new(name_symbol)
90
+ self
91
+ end
92
+
93
+ alias_method :and_controller, :with_controller
94
+
95
+ def only_methods(*methods)
96
+ validate_context
97
+
98
+ current_controller.access_methods = paths_for(current_controller.name,
99
+ *methods)
100
+ @current_context = Lockdown::RootContext.new(@name)
101
+ self
102
+ end
103
+
104
+ def except_methods(*methods)
105
+ validate_context
106
+
107
+ current_controller.access_methods = current_controller.access_methods - paths_for(current_controller.name, *methods)
108
+
109
+ @current_context = Lockdown::RootContext.new(@name)
110
+ self
111
+ end
112
+
113
+ def to_model(name_symbol)
114
+ validate_context
115
+
116
+ @models[name_symbol] = Model.new(name_symbol)
117
+ @current_context = Lockdown::ModelContext.new(name_symbol)
118
+ self
119
+ end
120
+
121
+ def where(controller_method)
122
+ validate_context
123
+
124
+ @current_context = Lockdown::ModelWhereContext.new(current_context.name)
125
+ self
126
+ end
127
+
128
+ def equals(model_method)
129
+ validate_context
130
+
131
+ associate_model_method(model_method, :equals)
132
+ @current_context = Lockdown::RootContext.new(@name)
133
+ self
134
+ end
135
+
136
+ def is_in(model_method)
137
+ validate_context
138
+
139
+ associate_model_method(model_method, :includes)
140
+ @current_context = Lockdown::RootContext.new(@name)
141
+ self
142
+ end
143
+
144
+ alias_method :includes, :is_in
145
+
146
+ def public_access?
147
+ @public_access
148
+ end
149
+
150
+ def protected_access?
151
+ @protected_access
152
+ end
153
+
154
+ def set_as_public_access
155
+ if protected_access?
156
+ raise PermissionScopeCollision, "Permission: #{name} already marked as protected and trying to set as public."
157
+ end
158
+ @public_access = true
159
+ end
160
+
161
+ def set_as_protected_access
162
+ if public_access?
163
+ raise PermissionScopeCollision, "Permission: #{name} already marked as public and trying to set as protected."
164
+ end
165
+ @protected_access = true
166
+ end
167
+
168
+ def current_context
169
+ @current_context
170
+ end
171
+
172
+ def current_controller
173
+ @controllers[current_context.name]
174
+ end
175
+
176
+ def current_model
177
+ @models[current_context.name]
178
+ end
179
+
180
+ def ==(other)
181
+ name == other.name
182
+ end
183
+
184
+ private
185
+
186
+ def associate_model_method(model_method, association)
187
+ current_model.model_method = model_method
188
+ current_model.association = association
189
+ @current_context = Lockdown::RootContext.new(@name)
190
+ end
191
+
192
+ def validate_context
193
+ method_trace = caller.first;
194
+ calling_method = caller.first[/#{__FILE__}:(\d+):in `(.*)'/,2]
195
+ unless current_context.allows?(calling_method)
196
+ raise InvalidRuleContext, "Method: #{calling_method} was called on wrong context #{current_context}. Allowed methods are: #{current_context.allowed_methods.join(',')}."
197
+ end
198
+ end
199
+
200
+ def paths_for(controller, *methods)
201
+ Lockdown::System.paths_for(controller, *methods)
202
+ end
203
+ end
204
+ end