andrewzielinski-lockdown 0.9.6

Sign up to get free protection for your applications and to get access to all the features.
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,68 @@
1
+ module Lockdown
2
+ module Orms
3
+ module ActiveRecord
4
+ class << self
5
+ def use_me?
6
+ Object.const_defined?("ActiveRecord") && ::ActiveRecord.const_defined?("Base")
7
+ end
8
+
9
+ def included(mod)
10
+ mod.extend Lockdown::Orms::ActiveRecord::Helper
11
+ mixin
12
+ end
13
+
14
+ def mixin
15
+ Lockdown.orm_parent.class_eval do
16
+ include Lockdown::Orms::ActiveRecord::Stamps
17
+ end
18
+ end
19
+ end # class block
20
+
21
+ module Helper
22
+ def orm_parent
23
+ ::ActiveRecord::Base
24
+ end
25
+
26
+ def database_execute(query)
27
+ orm_parent.connection.execute(query)
28
+ end
29
+
30
+ def database_query(query)
31
+ orm_parent.connection.execute(query)
32
+ end
33
+
34
+ def database_table_exists?(klass)
35
+ klass.table_exists?
36
+ end
37
+ end
38
+
39
+ module Stamps
40
+ def self.included(base)
41
+ base.class_eval do
42
+ alias_method :create_without_stamps, :create
43
+ alias_method :create, :create_with_stamps
44
+ alias_method :update_without_stamps, :update
45
+ alias_method :update, :update_with_stamps
46
+ end
47
+ end
48
+
49
+ def current_who_did_it
50
+ Thread.current[:who_did_it]
51
+ end
52
+
53
+ def create_with_stamps
54
+ pid = current_who_did_it || Lockdown::System.fetch(:default_who_did_it)
55
+ self[:created_by] = pid if self.respond_to?(:created_by)
56
+ self[:updated_by] = pid if self.respond_to?(:updated_by)
57
+ create_without_stamps
58
+ end
59
+
60
+ def update_with_stamps
61
+ pid = current_who_did_it || Lockdown::System.fetch(:default_who_did_it)
62
+ self[:updated_by] = pid if self.respond_to?(:updated_by)
63
+ update_without_stamps
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -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
@@ -0,0 +1,289 @@
1
+ module Lockdown
2
+ class InvalidRuleAssignment < StandardError; end
3
+
4
+ module Rules
5
+ attr_accessor :options
6
+ attr_accessor :permissions
7
+ attr_accessor :user_groups
8
+ attr_accessor :controller_classes
9
+
10
+ attr_reader :protected_access
11
+ attr_reader :public_access
12
+
13
+ attr_reader :permission_objects
14
+
15
+ def set_defaults
16
+ @permissions = {}
17
+ @user_groups = {}
18
+ @options = {}
19
+
20
+ @permission_objects = {}
21
+
22
+ @controller_classes = []
23
+ @public_access = []
24
+ @protected_access = []
25
+
26
+ @options = {
27
+ :session_timeout => (60 * 60),
28
+ :who_did_it => :current_user_id,
29
+ :default_who_did_it => 1,
30
+ :logout_on_access_violation => false,
31
+ :access_denied_path => "/",
32
+ :successful_login_path => "/",
33
+ :subdirectory => nil,
34
+ :skip_db_sync_in => ["test"],
35
+ :link_separator => ' | '
36
+ }
37
+ end
38
+
39
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
40
+ # =Rule defining methods. e.g. Methods used in lib/lockdown/init.rb
41
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
42
+
43
+ # Creates new permission object
44
+ # Refer to the Permission object for the full functionality
45
+ def set_permission(name)
46
+ @permission_objects[name] = Lockdown::Permission.new(name)
47
+ end
48
+
49
+ # Defines public access by the permission symbols
50
+ #
51
+ # ==== Example
52
+ # set_public_access(:permission_one, :permission_two)
53
+ #
54
+ def set_public_access(*perms)
55
+ perms.each do |perm_symbol|
56
+ perm = permission_objects.find{|name, pobj| pobj.name == perm_symbol}
57
+ if perm
58
+ perm[1].set_as_public_access
59
+ else
60
+ msg = "Permission not found: #{perm_symbol}"
61
+ raise InvalidRuleAssigment, msg
62
+ end
63
+ end
64
+ end
65
+
66
+ # Defines protected access by the permission symbols
67
+ #
68
+ # ==== Example
69
+ # set_public_access(:permission_one, :permission_two)
70
+ #
71
+ def set_protected_access(*perms)
72
+ perms.each do |perm_symbol|
73
+ perm = permission_objects.find{|name, pobj| pobj.name == perm_symbol}
74
+ if perm
75
+ perm[1].set_as_protected_access
76
+ else
77
+ msg = "Permission not found: #{perm_symbol}"
78
+ raise InvalidRuleAssigment, msg
79
+ end
80
+ end
81
+ end
82
+
83
+ # Define a user groups by name and permission symbol(s)
84
+ #
85
+ # ==== Example
86
+ # set_user_group(:managment_group, :permission_one, :permission_two)
87
+ #
88
+ def set_user_group(name, *perms)
89
+ user_groups[name] ||= []
90
+ perms.each do |perm|
91
+ user_groups[name].push(perm)
92
+ end
93
+ end
94
+
95
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
96
+ # =Convenience methods for permissions and user groups
97
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
98
+
99
+ # Returns array of permission names as symbols
100
+ def get_permissions
101
+ permissions.keys
102
+ end
103
+
104
+ # Is the permission defined?
105
+ def permission_exists?(permission_symbol)
106
+ get_permissions.include?(permission_symbol)
107
+ end
108
+
109
+ alias_method :has_permission?, :permission_exists?
110
+
111
+ # returns true if the permission is public
112
+ def public_access?(permmision_symbol)
113
+ public_access.include?(permmision_symbol)
114
+ end
115
+
116
+ # returns true if the permission is public
117
+ def protected_access?(permmision_symbol)
118
+ protected_access.include?(permmision_symbol)
119
+ end
120
+
121
+ # These permissions are assigned by the system
122
+ def permission_assigned_automatically?(permmision_symbol)
123
+ public_access?(permmision_symbol) || protected_access?(permmision_symbol)
124
+ end
125
+
126
+ # Returns array of user group names as symbols
127
+ def get_user_groups
128
+ user_groups.keys
129
+ end
130
+
131
+ # Is the user group defined?
132
+ # The :administrators user group always exists
133
+ def user_group_exists?(user_group_symbol)
134
+ return true if user_group_symbol == Lockdown.administrator_group_symbol
135
+ get_user_groups.include?(user_group_symbol)
136
+ end
137
+
138
+ alias_method :has_user_group?, :user_group_exists?
139
+
140
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
141
+ # =Convenience methods for permissions and user groups
142
+ #~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
143
+
144
+ # Pass in a user object to be associated to the administrator user group
145
+ # The group will be created if it doesn't exist
146
+ def make_user_administrator(usr)
147
+ usr.user_groups << UserGroup.
148
+ find_or_create_by_name(Lockdown.administrator_group_string)
149
+ end
150
+
151
+
152
+ # Returns array of controller/action values all logged in users can access.
153
+ def standard_authorized_user_rights
154
+ public_access + protected_access
155
+ end
156
+
157
+ # Return array of controller/action values user can access.
158
+ def access_rights_for_user(usr)
159
+ return unless usr
160
+ return :all if administrator?(usr)
161
+
162
+ rights = standard_authorized_user_rights
163
+
164
+ usr.user_groups.each do |grp|
165
+ permissions_for_user_group(grp).each do |perm|
166
+ rights += access_rights_for_permission(perm)
167
+ end
168
+ end
169
+ rights
170
+ end
171
+
172
+ # Return array of controller/action for a permission
173
+ def access_rights_for_permission(perm)
174
+ sym = Lockdown.get_symbol(perm)
175
+
176
+ permissions[sym]
177
+ rescue
178
+ raise SecurityError, "Permission requested is not defined: #{sym}"
179
+ end
180
+
181
+
182
+ # Test user for administrator rights
183
+ def administrator?(usr)
184
+ user_has_user_group?(usr, Lockdown.administrator_group_symbol)
185
+ end
186
+
187
+ # Pass in user object and symbol for name of user group
188
+ def user_has_user_group?(usr, sym)
189
+ usr.user_groups.any? do |ug|
190
+ Lockdown.convert_reference_name(ug.name) == sym
191
+ end
192
+ end
193
+
194
+ # Use this for the management screen to restrict user group list to the
195
+ # user. This will prevent a user from creating a user with more power than
196
+ # him/her self.
197
+ def user_groups_assignable_for_user(usr)
198
+ return [] if usr.nil?
199
+
200
+ if administrator?(usr)
201
+ UserGroup.find_by_sql <<-SQL
202
+ select user_groups.* from user_groups order by user_groups.name
203
+ SQL
204
+ else
205
+ UserGroup.find_by_sql <<-SQL
206
+ select user_groups.* from user_groups, user_groups_users
207
+ where user_groups.id = user_groups_users.user_group_id
208
+ and user_groups_users.user_id = #{usr.id}
209
+ order by user_groups.name
210
+ SQL
211
+ end
212
+ end
213
+
214
+ # Similar to user_groups_assignable_for_user, this method should be
215
+ # used to restrict users from creating a user group with more power than
216
+ # they have been allowed.
217
+ def permissions_assignable_for_user(usr)
218
+ return [] if usr.nil?
219
+ if administrator?(usr)
220
+ get_permissions.collect do |k|
221
+ ::Permission.find_by_name(Lockdown.get_string(k))
222
+ end.compact
223
+ else
224
+ user_groups_assignable_for_user(usr).collect do |g|
225
+ g.permissions
226
+ end.flatten.compact
227
+ end
228
+ end
229
+
230
+ # Returns and array of permission symbols for the user group
231
+ def permissions_for_user_group(ug)
232
+ sym = Lockdown.get_symbol(ug)
233
+ perm_array = []
234
+
235
+ if has_user_group?(sym)
236
+ permissions = user_groups[sym] || []
237
+ else
238
+ permissions = ug.permissions
239
+ end
240
+
241
+
242
+ permissions.each do |perm|
243
+ perm_sym = Lockdown.get_symbol(perm)
244
+
245
+ unless permission_exists?(perm_sym)
246
+ msg = "Permission associated to User Group is invalid: #{perm}"
247
+ raise SecurityError, msg
248
+ end
249
+
250
+ perm_array << perm_sym
251
+ end
252
+
253
+ perm_array
254
+ end
255
+
256
+ def process_rules
257
+ parse_permissions
258
+ validate_user_groups
259
+ end
260
+
261
+ private
262
+
263
+ def parse_permissions
264
+ permission_objects.each do |name, perm|
265
+ @permissions[perm.name] ||= []
266
+ perm.controllers.each do |name, controller|
267
+ @permissions[perm.name] |= controller.access_methods
268
+
269
+ if perm.public_access?
270
+ @public_access |= controller.access_methods
271
+ elsif perm.protected_access?
272
+ @protected_access |= controller.access_methods
273
+ end
274
+ end
275
+ end
276
+ end
277
+
278
+ def validate_user_groups
279
+ user_groups.each do |user_group, perms|
280
+ perms.each do |perm|
281
+ unless permission_exists?(perm)
282
+ msg ="User Group: #{user_group}, permission not found: #{perm}"
283
+ raise InvalidRuleAssignment, msg
284
+ end
285
+ end
286
+ end
287
+ end
288
+ end
289
+ end