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,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