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.
- data/.DS_Store +0 -0
- data/History.txt +3 -0
- data/README.txt +1 -1
- data/Rakefile +16 -1
- data/lib/lockdown/context.rb +41 -0
- data/lib/lockdown/database.rb +11 -14
- data/lib/lockdown/frameworks/rails/controller.rb +57 -4
- data/lib/lockdown/frameworks/rails/view.rb +1 -1
- data/lib/lockdown/frameworks/rails.rb +21 -10
- data/lib/lockdown/helper.rb +1 -1
- data/lib/lockdown/permission.rb +204 -0
- data/lib/lockdown/rules.rb +287 -0
- data/lib/lockdown/session.rb +8 -6
- data/lib/lockdown/system.rb +35 -88
- data/lib/lockdown.rb +52 -49
- data/rails_generators/.DS_Store +0 -0
- data/rails_generators/lockdown/.DS_Store +0 -0
- data/rails_generators/lockdown/lockdown_generator.rb +5 -5
- data/rails_generators/lockdown/templates/.DS_Store +0 -0
- data/rails_generators/lockdown/templates/lib/.DS_Store +0 -0
- data/rails_generators/lockdown/templates/lib/lockdown/init.rb +27 -19
- data/rails_generators/lockdown/templates/lib/lockdown/session.rb +1 -3
- data/spec/lockdown/database_spec.rb +158 -0
- data/spec/lockdown/frameworks/rails/controller_spec.rb +220 -0
- data/spec/lockdown/frameworks/rails/view_spec.rb +87 -0
- data/spec/lockdown/frameworks/rails_spec.rb +170 -0
- data/spec/lockdown/permission_spec.rb +156 -0
- data/spec/lockdown/rules_spec.rb +109 -0
- data/spec/lockdown/session_spec.rb +88 -0
- data/spec/lockdown/system_spec.rb +59 -0
- data/spec/lockdown_spec.rb +19 -0
- data/spec/rcov.opts +5 -0
- data/spec/spec.opts +3 -0
- data/spec/spec_helper.rb +1 -0
- data/tasks/post_load.rake +2 -7
- data/tasks/setup.rb +24 -3
- metadata +23 -12
- data/.gitignore +0 -5
- data/Manifest.txt +0 -51
- data/lib/lockdown/controller.rb +0 -64
- data/lib/lockdown/frameworks/merb/controller.rb +0 -63
- data/lib/lockdown/frameworks/merb/view.rb +0 -32
- data/lib/lockdown/frameworks/merb.rb +0 -84
- data/lib/lockdown/orms/data_mapper.rb +0 -70
- data/lib/lockdown/rights.rb +0 -208
- 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
data/Rakefile
CHANGED
@@ -12,7 +12,20 @@ end
|
|
12
12
|
ensure_in_path 'lib'
|
13
13
|
require 'lockdown'
|
14
14
|
|
15
|
-
task :default => '
|
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
|
data/lib/lockdown/database.rb
CHANGED
@@ -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 = ?",
|
71
|
-
|
72
|
-
|
73
|
-
|
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
|
-
|
41
|
-
|
42
|
-
|
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
|
@@ -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
|
84
|
-
|
87
|
+
if ::Rails::VERSION::MAJOR >= 2 && ::Rails::VERSION::MINOR >= 3
|
88
|
+
filename = "application_controller.rb"
|
85
89
|
else
|
86
|
-
|
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
|
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(
|
106
|
+
ActiveSupport::Dependencies.require_or_load(filename)
|
94
107
|
else
|
95
|
-
Dependencies.require_or_load(
|
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
|
data/lib/lockdown/helper.rb
CHANGED
@@ -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
|