patrick-lockdown 2.0.4.1
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/README.md +42 -0
- data/Rakefile +54 -0
- data/lib/lockdown.rb +42 -0
- data/lib/lockdown/access.rb +108 -0
- data/lib/lockdown/configuration.rb +209 -0
- data/lib/lockdown/database.rb +122 -0
- data/lib/lockdown/delivery.rb +28 -0
- data/lib/lockdown/errors.rb +7 -0
- data/lib/lockdown/frameworks/rails.rb +77 -0
- data/lib/lockdown/frameworks/rails/controller.rb +145 -0
- data/lib/lockdown/frameworks/rails/view.rb +51 -0
- data/lib/lockdown/helper.rb +40 -0
- data/lib/lockdown/orms/active_record.rb +66 -0
- data/lib/lockdown/permission.rb +56 -0
- data/lib/lockdown/resource.rb +54 -0
- data/lib/lockdown/session.rb +50 -0
- data/lib/lockdown/user_group.rb +16 -0
- data/patrick-lockdown.gemspec +62 -0
- data/tags +142 -0
- data/test/helper.rb +10 -0
- data/test/lockdown/test_access.rb +80 -0
- data/test/lockdown/test_configuration.rb +195 -0
- data/test/lockdown/test_delivery.rb +224 -0
- data/test/lockdown/test_helper.rb +33 -0
- data/test/lockdown/test_permission.rb +73 -0
- data/test/lockdown/test_resource.rb +47 -0
- data/test/lockdown/test_session.rb +31 -0
- data/test/lockdown/test_user_group.rb +17 -0
- metadata +96 -0
@@ -0,0 +1,122 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Lockdown
|
4
|
+
class Database
|
5
|
+
class << self
|
6
|
+
# This is very basic and could be handled better using orm specific
|
7
|
+
# functionality, but I wanted to keep it generic to avoid creating
|
8
|
+
# an interface for each the different orm implementations.
|
9
|
+
# We'll see how it works...
|
10
|
+
def sync_with_db
|
11
|
+
@permissions = Lockdown::Configuration.permission_names
|
12
|
+
@user_groups = Lockdown::Configuration.user_group_names
|
13
|
+
|
14
|
+
unless ::Permission.table_exists? && Lockdown.user_group_class.table_exists?
|
15
|
+
Lockdown.logger.info ">> Lockdown tables not found. Skipping database sync."
|
16
|
+
return
|
17
|
+
end
|
18
|
+
|
19
|
+
create_new_permissions
|
20
|
+
|
21
|
+
delete_extinct_permissions
|
22
|
+
|
23
|
+
maintain_user_groups
|
24
|
+
end
|
25
|
+
|
26
|
+
# Create permissions not found in the database
|
27
|
+
def create_new_permissions
|
28
|
+
@permissions.each do |name|
|
29
|
+
next if Lockdown::Configuration.permission_assigned_automatically?(name)
|
30
|
+
p = ::Permission.find(:first, :conditions => ["name = ?", name])
|
31
|
+
unless p
|
32
|
+
Lockdown.logger.info ">> Lockdown: Permission not found in db: #{name}, creating."
|
33
|
+
::Permission.create(:name => name)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Delete the permissions not found in init.rb
|
39
|
+
def delete_extinct_permissions
|
40
|
+
db_perms = ::Permission.find(:all).dup
|
41
|
+
db_perms.each do |dbp|
|
42
|
+
unless @permissions.include?(dbp.name)
|
43
|
+
Lockdown.logger.info ">> Lockdown: Permission no longer in init.rb: #{dbp.name}, deleting."
|
44
|
+
ug_table = Lockdown.user_groups_hbtm_reference.to_s
|
45
|
+
if "permissions" < ug_table
|
46
|
+
join_table = "permissions_#{ug_table}"
|
47
|
+
else
|
48
|
+
join_table = "#{ug_table}_permissions"
|
49
|
+
end
|
50
|
+
Lockdown.database_execute("delete from #{join_table} where permission_id = #{dbp.id}")
|
51
|
+
dbp.destroy
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def maintain_user_groups
|
57
|
+
# Create user groups not found in the database
|
58
|
+
@user_groups.each do |name|
|
59
|
+
unless ug = Lockdown.user_group_class.find(:first, :conditions => ["name = ?", name])
|
60
|
+
create_user_group(name)
|
61
|
+
else
|
62
|
+
# Remove permissions from user group not found in init.rb
|
63
|
+
remove_invalid_permissions(ug)
|
64
|
+
|
65
|
+
# Add in permissions from init.rb not found in database
|
66
|
+
add_valid_permissions(ug)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def create_user_group(name)
|
72
|
+
Lockdown.logger.info ">> Lockdown: #{Lockdown::Configuration.user_group_model} not in the db: #{name}, creating."
|
73
|
+
ug = Lockdown.user_group_class.create(:name => name)
|
74
|
+
#Inefficient, definitely, but shouldn't have any issues across orms.
|
75
|
+
#
|
76
|
+
Lockdown::Configuration.user_group_permissions_names(name).each do |perm|
|
77
|
+
|
78
|
+
if Lockdown::Configuration.permission_assigned_automatically?(perm)
|
79
|
+
Lockdown.logger.info ">> Permission #{perm} cannot be assigned to #{name}. Already belongs to built in user group (public or protected)."
|
80
|
+
raise InvalidPermissionAssignment, "Invalid permission assignment"
|
81
|
+
end
|
82
|
+
|
83
|
+
p = ::Permission.find(:first, :conditions => ["name = ?", perm])
|
84
|
+
|
85
|
+
ug_table = Lockdown.user_groups_hbtm_reference.to_s
|
86
|
+
if "permissions" < ug_table
|
87
|
+
join_table = "permissions_#{ug_table}"
|
88
|
+
else
|
89
|
+
join_table = "#{ug_table}_permissions"
|
90
|
+
end
|
91
|
+
Lockdown.database_execute "insert into #{join_table}(permission_id, #{Lockdown.user_group_id_reference}) values(#{p.id}, #{ug.id})"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def remove_invalid_permissions(ug)
|
96
|
+
ug.permissions.each do |perm|
|
97
|
+
unless Lockdown::Configuration.user_group_permissions_names(ug.name).include?(perm.name)
|
98
|
+
Lockdown.logger.info ">> Lockdown: Permission: #{perm.name} no longer associated to User Group: #{ug.name}, deleting."
|
99
|
+
ug.permissions.delete(perm)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def add_valid_permissions(ug)
|
105
|
+
Lockdown::Configuration.user_group_permissions_names(ug.name).each do |perm_name|
|
106
|
+
found = false
|
107
|
+
# see if permission exists
|
108
|
+
ug.permissions.each do |p|
|
109
|
+
found = true if p.name == perm_name
|
110
|
+
end
|
111
|
+
# if not found, add it
|
112
|
+
unless found
|
113
|
+
Lockdown.logger.info ">> Lockdown: Permission: #{perm_name} not found for User Group: #{ug.name}, adding it."
|
114
|
+
p = ::Permission.find(:first, :conditions => ["name = ?", perm_name])
|
115
|
+
ug.permissions << p
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end # class block
|
121
|
+
end # Database
|
122
|
+
end #Lockdown
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Lockdown
|
4
|
+
class Delivery
|
5
|
+
class << self
|
6
|
+
# @return [true|false] if the given path is allowed
|
7
|
+
def allowed?(path, access_rights = nil)
|
8
|
+
begin
|
9
|
+
::Authorization.configure
|
10
|
+
rescue NameError
|
11
|
+
end
|
12
|
+
|
13
|
+
access_rights ||= Lockdown::Configuration.public_access
|
14
|
+
|
15
|
+
access_rights_regex = Lockdown.regex(access_rights)
|
16
|
+
|
17
|
+
path += "/" unless path =~ /\/$/
|
18
|
+
path = "/" + path unless path =~ /^\//
|
19
|
+
|
20
|
+
if (access_rights_regex =~ path) == 0
|
21
|
+
return true
|
22
|
+
end
|
23
|
+
|
24
|
+
return false
|
25
|
+
end
|
26
|
+
end # class block
|
27
|
+
end # Delivery
|
28
|
+
end # Lockdown
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require File.join(File.dirname(__FILE__), "rails", "controller")
|
4
|
+
require File.join(File.dirname(__FILE__), "rails", "view")
|
5
|
+
|
6
|
+
module Lockdown
|
7
|
+
module Frameworks
|
8
|
+
module Rails
|
9
|
+
class << self
|
10
|
+
def included(mod)
|
11
|
+
mod.extend Lockdown::Frameworks::Rails::Environment
|
12
|
+
mixin
|
13
|
+
end
|
14
|
+
|
15
|
+
def mixin
|
16
|
+
mixin_controller
|
17
|
+
|
18
|
+
Lockdown.view_helper.class_eval do
|
19
|
+
include Lockdown::Frameworks::Rails::View
|
20
|
+
end
|
21
|
+
|
22
|
+
Lockdown::Configuration.class_eval do
|
23
|
+
def self.skip_sync?
|
24
|
+
skip_db_sync_in.include?(::Rails.env)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def mixin_controller(klass = Lockdown.controller_parent)
|
30
|
+
klass.class_eval do
|
31
|
+
include Lockdown::Session
|
32
|
+
include Lockdown::Frameworks::Rails::Controller::Lock
|
33
|
+
end
|
34
|
+
|
35
|
+
klass.helper_method :authorized?
|
36
|
+
|
37
|
+
klass.hide_action(:set_current_user, :configure_lockdown, :check_request_authorization)
|
38
|
+
|
39
|
+
klass.before_filter do |c|
|
40
|
+
c.set_current_user
|
41
|
+
c.configure_lockdown
|
42
|
+
c.check_request_authorization
|
43
|
+
end
|
44
|
+
|
45
|
+
klass.filter_parameter_logging :password, :password_confirmation
|
46
|
+
|
47
|
+
klass.rescue_from SecurityError, :with => proc{|e| ld_access_denied(e)}
|
48
|
+
end
|
49
|
+
end # class block
|
50
|
+
|
51
|
+
module Environment
|
52
|
+
|
53
|
+
def project_root
|
54
|
+
::RAILS_ROOT
|
55
|
+
end
|
56
|
+
|
57
|
+
def view_helper
|
58
|
+
::ActionView::Base
|
59
|
+
end
|
60
|
+
|
61
|
+
# cache_classes is true in production and testing, need to
|
62
|
+
# modify the ApplicationController
|
63
|
+
def controller_parent
|
64
|
+
if caching?
|
65
|
+
ApplicationController
|
66
|
+
else
|
67
|
+
ActionController::Base
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def caching?
|
72
|
+
::Rails.configuration.cache_classes
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end # Rails
|
76
|
+
end # Frameworks
|
77
|
+
end # Lockdown
|
@@ -0,0 +1,145 @@
|
|
1
|
+
module Lockdown
|
2
|
+
module Frameworks
|
3
|
+
module Rails
|
4
|
+
module Controller
|
5
|
+
# Locking methods
|
6
|
+
module Lock
|
7
|
+
|
8
|
+
def configure_lockdown
|
9
|
+
store_location
|
10
|
+
end
|
11
|
+
|
12
|
+
# Basic auth functionality needs to be reworked as
|
13
|
+
# Lockdown doesn't provide authentication functionality.
|
14
|
+
def set_current_user
|
15
|
+
if logged_in?
|
16
|
+
whodat = send(Lockdown::Configuration.who_did_it)
|
17
|
+
Thread.current[:who_did_it] = whodat
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def check_request_authorization
|
22
|
+
unless authorized?(path_from_hash(params))
|
23
|
+
parameters = respond_to?(:filter_parameters) ? filter_parameters(params) : params.dup
|
24
|
+
raise SecurityError, "Authorization failed! \nparams: #{parameters.inspect}\nsession: #{session.inspect}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
protected
|
29
|
+
|
30
|
+
def store_location
|
31
|
+
if request.get? && (session[:thispage] != sent_from_uri)
|
32
|
+
session[:prevpage] = session[:thispage] || ''
|
33
|
+
session[:thispage] = sent_from_uri
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def sent_from_uri
|
38
|
+
request.fullpath
|
39
|
+
end
|
40
|
+
|
41
|
+
def authorized?(url, method = nil)
|
42
|
+
# Reset access unless caching?
|
43
|
+
add_lockdown_session_values unless Lockdown.caching?
|
44
|
+
|
45
|
+
return false unless url
|
46
|
+
|
47
|
+
method ||= (params[:method] || request.method)
|
48
|
+
|
49
|
+
url_parts = URI::split(url.strip)
|
50
|
+
|
51
|
+
path = url_parts[5]
|
52
|
+
|
53
|
+
subdir = Lockdown::Configuration.subdirectory
|
54
|
+
if subdir && subdir == path[1,subdir.length]
|
55
|
+
path = path[(subdir.length+1)..-1]
|
56
|
+
end
|
57
|
+
|
58
|
+
if Lockdown::Delivery.allowed?(path, session[:access_rights])
|
59
|
+
return true
|
60
|
+
end
|
61
|
+
|
62
|
+
path_parts = path.split('/')
|
63
|
+
|
64
|
+
if path_parts.last == "index"
|
65
|
+
path_parts.pop
|
66
|
+
new_path = path_parts.join('/')
|
67
|
+
return Lockdown::Delivery.allowed?(new_path, session[:access_rights])
|
68
|
+
end
|
69
|
+
|
70
|
+
begin
|
71
|
+
if ::Rails.respond_to?(:application)
|
72
|
+
router = ::Rails.application.routes
|
73
|
+
else
|
74
|
+
router = ActionController::Routing::Routes
|
75
|
+
end
|
76
|
+
|
77
|
+
hash = router.recognize_path(path, :method => method)
|
78
|
+
|
79
|
+
if hash
|
80
|
+
return Lockdown::Delivery.allowed?(path_from_hash(hash),
|
81
|
+
session[:access_rights])
|
82
|
+
end
|
83
|
+
rescue ActionController::RoutingError
|
84
|
+
# continue on
|
85
|
+
end
|
86
|
+
|
87
|
+
# Mailto link
|
88
|
+
if url =~ /^mailto:/
|
89
|
+
return true
|
90
|
+
end
|
91
|
+
|
92
|
+
# Public file
|
93
|
+
file = File.join(::Rails.root, 'public', url)
|
94
|
+
if File.exists?(file)
|
95
|
+
return true
|
96
|
+
end
|
97
|
+
|
98
|
+
# Passing in different domain
|
99
|
+
return remote_url?(url_parts[2])
|
100
|
+
end
|
101
|
+
|
102
|
+
def ld_access_denied(e)
|
103
|
+
|
104
|
+
Lockdown.logger.info "Access denied: #{e}"
|
105
|
+
|
106
|
+
if Lockdown::Configuration.logout_on_access_violation
|
107
|
+
reset_session
|
108
|
+
end
|
109
|
+
respond_to do |format|
|
110
|
+
format.html do
|
111
|
+
store_location
|
112
|
+
redirect_to Lockdown::Configuration.access_denied_path
|
113
|
+
return
|
114
|
+
end
|
115
|
+
format.xml do
|
116
|
+
headers["Status"] = "Unauthorized"
|
117
|
+
headers["WWW-Authenticate"] = %(Basic realm="Web Password")
|
118
|
+
render :text => e.message, :status => "401 Unauthorized"
|
119
|
+
return
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
def path_from_hash(hash)
|
125
|
+
hash[:controller].to_s + "/" + hash[:action].to_s
|
126
|
+
end
|
127
|
+
|
128
|
+
def remote_url?(domain = nil)
|
129
|
+
return false if domain.nil? || domain.strip.length == 0
|
130
|
+
request.host.downcase != domain.downcase
|
131
|
+
end
|
132
|
+
|
133
|
+
def redirect_back_or_default(default)
|
134
|
+
if session[:prevpage].nil? || session[:prevpage].blank?
|
135
|
+
redirect_to(default)
|
136
|
+
else
|
137
|
+
redirect_to(session[:prevpage])
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end # Lock
|
141
|
+
end # Controller
|
142
|
+
end # Rails
|
143
|
+
end # Frameworks
|
144
|
+
end # Lockdown
|
145
|
+
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Lockdown
|
2
|
+
module Frameworks
|
3
|
+
module Rails
|
4
|
+
module View
|
5
|
+
def self.included(base)
|
6
|
+
base.class_eval do
|
7
|
+
alias_method :link_to_open, :link_to
|
8
|
+
alias_method :link_to, :link_to_secured
|
9
|
+
|
10
|
+
alias_method :button_to_open, :button_to
|
11
|
+
alias_method :button_to, :button_to_secured
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def link_to_secured(name, options = {}, html_options = nil)
|
16
|
+
url = url_for(options)
|
17
|
+
|
18
|
+
method = html_options && html_options[:method] ? html_options[:method] : :get
|
19
|
+
|
20
|
+
if authorized?(url, method)
|
21
|
+
return link_to_open(name, url, html_options)
|
22
|
+
end
|
23
|
+
return ""
|
24
|
+
end
|
25
|
+
|
26
|
+
def button_to_secured(name, options = {}, html_options = nil)
|
27
|
+
url = url_for(options)
|
28
|
+
|
29
|
+
method = html_options ? html_options[:method] : :get
|
30
|
+
|
31
|
+
if authorized?(url, method)
|
32
|
+
return button_to_open(name, url, html_options)
|
33
|
+
end
|
34
|
+
return ""
|
35
|
+
end
|
36
|
+
|
37
|
+
def link_to_or_show(name, options = {}, html_options = nil)
|
38
|
+
lnk = link_to(name, options, html_options)
|
39
|
+
lnk.length == 0 ? name : lnk
|
40
|
+
end
|
41
|
+
|
42
|
+
def links(*lis)
|
43
|
+
rvalue = []
|
44
|
+
lis.each{|link| rvalue << link if link.length > 0 }
|
45
|
+
rvalue.join( Lockdown::Configuration.link_separator )
|
46
|
+
end
|
47
|
+
end # View
|
48
|
+
end # Rails
|
49
|
+
end # Frameworks
|
50
|
+
end # Lockdown
|
51
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'active_support/core_ext'
|
4
|
+
|
5
|
+
module Lockdown
|
6
|
+
module Helper
|
7
|
+
# @return [Regexp] with \A \z boundaries
|
8
|
+
def regex(string)
|
9
|
+
Regexp.new(/\A#{string}\z/)
|
10
|
+
end
|
11
|
+
|
12
|
+
def administrator_group_name
|
13
|
+
'Administrators'
|
14
|
+
end
|
15
|
+
|
16
|
+
def user_group_class
|
17
|
+
eval("::#{Lockdown::Configuration.user_group_model}")
|
18
|
+
end
|
19
|
+
|
20
|
+
def user_groups_hbtm_reference
|
21
|
+
Lockdown::Configuration.user_group_model.underscore.pluralize.to_sym
|
22
|
+
end
|
23
|
+
|
24
|
+
def user_group_id_reference
|
25
|
+
Lockdown::Configuration.user_group_model.underscore + "_id"
|
26
|
+
end
|
27
|
+
|
28
|
+
def user_class
|
29
|
+
eval("::#{Lockdown::Configuration.user_model}")
|
30
|
+
end
|
31
|
+
|
32
|
+
def users_hbtm_reference
|
33
|
+
Lockdown::Configuration.user_model.underscore.pluralize.to_sym
|
34
|
+
end
|
35
|
+
|
36
|
+
def user_id_reference
|
37
|
+
Lockdown::Configuration.user_model.underscore + "_id"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|