lockdown_vail 1.6.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +6 -0
  2. data/README.txt +36 -0
  3. data/Rakefile +38 -0
  4. data/VERSION +1 -0
  5. data/lib/lockdown/context.rb +41 -0
  6. data/lib/lockdown/database.rb +41 -0
  7. data/lib/lockdown/errors.rb +11 -0
  8. data/lib/lockdown/frameworks/rails/controller.rb +187 -0
  9. data/lib/lockdown/frameworks/rails/view.rb +50 -0
  10. data/lib/lockdown/frameworks/rails.rb +114 -0
  11. data/lib/lockdown/helper.rb +111 -0
  12. data/lib/lockdown/orms/active_record.rb +68 -0
  13. data/lib/lockdown/permission.rb +222 -0
  14. data/lib/lockdown/references.rb +19 -0
  15. data/lib/lockdown/rspec_helper.rb +114 -0
  16. data/lib/lockdown/rules.rb +372 -0
  17. data/lib/lockdown/session.rb +66 -0
  18. data/lib/lockdown/system.rb +58 -0
  19. data/lib/lockdown.rb +87 -0
  20. data/lockdown.gemspec +118 -0
  21. data/lockdown_vail.gemspec +120 -0
  22. data/rails_generators/lockdown/lockdown_generator.rb +274 -0
  23. data/rails_generators/lockdown/templates/app/controllers/permissions_controller.rb +22 -0
  24. data/rails_generators/lockdown/templates/app/controllers/sessions_controller.rb +39 -0
  25. data/rails_generators/lockdown/templates/app/controllers/user_groups_controller.rb +122 -0
  26. data/rails_generators/lockdown/templates/app/controllers/users_controller.rb +117 -0
  27. data/rails_generators/lockdown/templates/app/helpers/permissions_helper.rb +2 -0
  28. data/rails_generators/lockdown/templates/app/helpers/user_groups_helper.rb +2 -0
  29. data/rails_generators/lockdown/templates/app/helpers/users_helper.rb +2 -0
  30. data/rails_generators/lockdown/templates/app/models/permission.rb +13 -0
  31. data/rails_generators/lockdown/templates/app/models/profile.rb +10 -0
  32. data/rails_generators/lockdown/templates/app/models/user.rb +95 -0
  33. data/rails_generators/lockdown/templates/app/models/user_group.rb +15 -0
  34. data/rails_generators/lockdown/templates/app/views/permissions/index.html.erb +16 -0
  35. data/rails_generators/lockdown/templates/app/views/permissions/show.html.erb +26 -0
  36. data/rails_generators/lockdown/templates/app/views/sessions/new.html.erb +12 -0
  37. data/rails_generators/lockdown/templates/app/views/user_groups/edit.html.erb +33 -0
  38. data/rails_generators/lockdown/templates/app/views/user_groups/index.html.erb +20 -0
  39. data/rails_generators/lockdown/templates/app/views/user_groups/new.html.erb +31 -0
  40. data/rails_generators/lockdown/templates/app/views/user_groups/show.html.erb +29 -0
  41. data/rails_generators/lockdown/templates/app/views/users/edit.html.erb +51 -0
  42. data/rails_generators/lockdown/templates/app/views/users/index.html.erb +22 -0
  43. data/rails_generators/lockdown/templates/app/views/users/new.html.erb +50 -0
  44. data/rails_generators/lockdown/templates/app/views/users/show.html.erb +33 -0
  45. data/rails_generators/lockdown/templates/config/initializers/lockit.rb +1 -0
  46. data/rails_generators/lockdown/templates/db/migrate/create_admin_user.rb +17 -0
  47. data/rails_generators/lockdown/templates/db/migrate/create_permissions.rb +19 -0
  48. data/rails_generators/lockdown/templates/db/migrate/create_profiles.rb +26 -0
  49. data/rails_generators/lockdown/templates/db/migrate/create_user_groups.rb +19 -0
  50. data/rails_generators/lockdown/templates/db/migrate/create_users.rb +17 -0
  51. data/rails_generators/lockdown/templates/lib/lockdown/README +42 -0
  52. data/rails_generators/lockdown/templates/lib/lockdown/init.rb +136 -0
  53. data/spec/lockdown/context_spec.rb +191 -0
  54. data/spec/lockdown/database_spec.rb +66 -0
  55. data/spec/lockdown/frameworks/rails/controller_spec.rb +240 -0
  56. data/spec/lockdown/frameworks/rails/view_spec.rb +87 -0
  57. data/spec/lockdown/frameworks/rails_spec.rb +163 -0
  58. data/spec/lockdown/permission_spec.rb +156 -0
  59. data/spec/lockdown/rspec_helper_spec.rb +41 -0
  60. data/spec/lockdown/rules_spec.rb +245 -0
  61. data/spec/lockdown/session_spec.rb +125 -0
  62. data/spec/lockdown/system_spec.rb +51 -0
  63. data/spec/lockdown_spec.rb +19 -0
  64. data/spec/rcov.opts +5 -0
  65. data/spec/spec.opts +3 -0
  66. data/spec/spec_helper.rb +8 -0
  67. metadata +140 -0
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.DS_Store
2
+ *.swp
3
+ pkg/**
4
+ doc/**
5
+ email.txt
6
+ coverage/**
data/README.txt ADDED
@@ -0,0 +1,36 @@
1
+ lockdown
2
+ by Andrew Stone
3
+ http://stonean.com
4
+
5
+ == DESCRIPTION:
6
+
7
+ Lockdown is an authorization system for RubyOnRails (ver >= 2.1).
8
+
9
+ == INSTALL:
10
+
11
+ sudo gem install lockdown
12
+
13
+ == LICENSE:
14
+
15
+ (The MIT License)
16
+
17
+ Copyright (c) 2009 Andrew Stone
18
+
19
+ Permission is hereby granted, free of charge, to any person obtaining
20
+ a copy of this software and associated documentation files (the
21
+ 'Software'), to deal in the Software without restriction, including
22
+ without limitation the rights to use, copy, modify, merge, publish,
23
+ distribute, sublicense, and/or sell copies of the Software, and to
24
+ permit persons to whom the Software is furnished to do so, subject to
25
+ the following conditions:
26
+
27
+ The above copyright notice and this permission notice shall be
28
+ included in all copies or substantial portions of the Software.
29
+
30
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
31
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
32
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
33
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
34
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
35
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
36
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rcov'
4
+ require 'spec/rake/spectask'
5
+
6
+ require 'lib/lockdown.rb'
7
+ task :default => 'rcov'
8
+
9
+ desc "Flog your code for Justice!"
10
+ task :flog do
11
+ sh('flog lib/**/*.rb')
12
+ end
13
+
14
+ desc "Run all specs and rcov in a non-sucky way"
15
+ Spec::Rake::SpecTask.new(:rcov) do |t|
16
+ t.spec_opts = IO.readlines("spec/spec.opts").map {|l| l.chomp.split " "}.flatten
17
+ t.spec_files = FileList['spec/**/*_spec.rb']
18
+ t.rcov = true
19
+ t.rcov_opts = IO.readlines("spec/rcov.opts").map {|l| l.chomp.split " "}.flatten
20
+ end
21
+
22
+ begin
23
+ require 'jeweler'
24
+ Jeweler::Tasks.new do |gemspec|
25
+ gemspec.name = "lockdown_vail"
26
+ gemspec.version = Lockdown.version
27
+ gemspec.rubyforge_project = "lockdown"
28
+ gemspec.summary = "Authorization system for Rails 2.x"
29
+ gemspec.description = "Restrict access to your controller actions. Supports basic model level restrictions as well"
30
+ gemspec.email = "andy@stonean.com"
31
+ gemspec.homepage = "http://stonean.com/wiki/lockdown"
32
+ gemspec.authors = ["Andrew Stone"]
33
+ gemspec.add_development_dependency('rspec')
34
+ end
35
+ Jeweler::GemcutterTasks.new
36
+ rescue LoadError
37
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
38
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.6.2
@@ -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
@@ -0,0 +1,41 @@
1
+ module Lockdown
2
+ class Database
3
+ class << self
4
+ # This is very basic and could be handled better using orm specific
5
+ # functionality, but I wanted to keep it generic to avoid creating
6
+ # an interface for each the different orm implementations.
7
+ # We'll see how it works...
8
+ def sync_with_db
9
+
10
+ @permissions = Lockdown::System.get_permissions
11
+ @user_groups = Lockdown::System.get_user_groups
12
+
13
+ unless Lockdown.user_group_class.table_exists?
14
+ Lockdown.logger.info ">> Lockdown tables not found. Skipping database sync."
15
+ return
16
+ end
17
+
18
+ maintain_user_groups
19
+ rescue Exception => e
20
+ Lockdown.logger.error ">> Lockdown sync failed: #{e.backtrace.join("\n")}"
21
+ end
22
+
23
+
24
+ def maintain_user_groups
25
+ # Create user groups not found in the database
26
+ @user_groups.each do |key|
27
+ str = Lockdown.get_string(key)
28
+ unless ug = Lockdown.user_group_class.find(:first, :conditions => ["name = ?", str])
29
+ create_user_group(str, key)
30
+ end
31
+ end
32
+ end
33
+
34
+ def create_user_group(name_str, key)
35
+ Lockdown.logger.info ">> Lockdown: #{Lockdown::System.fetch(:user_group_model)} not in the db: #{name_str}, creating."
36
+ ug = Lockdown.user_group_class.create(:name => name_str)
37
+ end
38
+
39
+ end # class block
40
+ end # Database
41
+ end #Lockdown
@@ -0,0 +1,11 @@
1
+ module Lockdown
2
+ class InvalidRuleAssignment < StandardError; end
3
+
4
+ class InvalidRuleContext < StandardError; end
5
+
6
+ class PermissionScopeCollision < StandardError; end
7
+
8
+ class InvalidPermissionAssignment < StandardError; end
9
+
10
+ class GroupUndefinedError < StandardError; end
11
+ end
@@ -0,0 +1,187 @@
1
+ module Lockdown
2
+ module Frameworks
3
+ module Rails
4
+ module Controller
5
+
6
+ def available_actions(klass)
7
+ klass.action_methods
8
+ end
9
+
10
+ def controller_name(klass)
11
+ klass.controller_name
12
+ end
13
+
14
+ # Locking methods
15
+ module Lock
16
+
17
+ class LockdownSessionExpired < Exception
18
+ end
19
+
20
+ def configure_lockdown
21
+ Lockdown.maybe_parse_init
22
+ check_session_expiry
23
+ store_location
24
+ end
25
+
26
+ # Basic auth functionality needs to be reworked as
27
+ # Lockdown doesn't provide authentication functionality.
28
+ def set_current_user
29
+ login_from_basic_auth? unless logged_in?
30
+ if logged_in?
31
+ Thread.current[:who_did_it] = Lockdown::System.
32
+ call(self, :who_did_it)
33
+ end
34
+ end
35
+
36
+ def check_request_authorization
37
+ unless authorized?(path_from_hash(params))
38
+ raise SecurityError, "Authorization failed! \nparams: #{params.inspect}\nsession: #{session.inspect}"
39
+ end
40
+ end
41
+
42
+ protected
43
+
44
+ def path_allowed?(url)
45
+ access_rights = Lockdown::System.public_access + access_rights_from_session
46
+ access_rights.include?(url)
47
+ end
48
+
49
+ def check_session_expiry
50
+ if session[:expiry_time] && session[:expiry_time] < Time.now
51
+ nil_lockdown_values
52
+ Lockdown::System.call(self, :session_timeout_method)
53
+ raise LockdownSessionExpired, "Authorization failed! \nSession expired."
54
+ end
55
+ session[:expiry_time] = Time.now + Lockdown::System.fetch(:session_timeout)
56
+ end
57
+
58
+ def store_location
59
+ if (request.method == :get) && (session[:thispage] != sent_from_uri)
60
+ session[:prevpage] = session[:thispage] || ''
61
+ session[:thispage] = sent_from_uri
62
+ end
63
+ end
64
+
65
+ def sent_from_uri
66
+ request.request_uri
67
+ end
68
+
69
+ def authorized?(url, method = nil)
70
+ # Reset access unless caching?
71
+ add_lockdown_session_values unless Lockdown.caching?
72
+
73
+ return false unless url
74
+
75
+ return true if current_user_is_admin?
76
+
77
+ method ||= (params[:method] || request.method)
78
+
79
+ url_parts = URI::split(url.strip)
80
+
81
+ path = url_parts[5]
82
+
83
+ return true if path_allowed?(path)
84
+
85
+ begin
86
+ hash = ActionController::Routing::Routes.recognize_path(path, :method => method)
87
+ return path_allowed?(path_from_hash(hash)) if hash
88
+ rescue Exception => e
89
+ # continue on
90
+ end
91
+
92
+ # Mailto link
93
+ return true if url =~ /^mailto:/
94
+
95
+ # Public file
96
+ file = File.join(RAILS_ROOT, 'public', url)
97
+ return true if File.exists?(file)
98
+
99
+ # Passing in different domain
100
+ return remote_url?(url_parts[2])
101
+ end
102
+
103
+ def ld_access_denied(e)
104
+
105
+ Lockdown.logger.info "Access denied: #{e}"
106
+
107
+ if Lockdown::System.fetch(:logout_on_access_violation)
108
+ reset_session
109
+ end
110
+ respond_to do |format|
111
+ format.html do
112
+ store_location
113
+ redirect_to Lockdown::System.fetch(:access_denied_path)
114
+ return
115
+ end
116
+ format.xml do
117
+ headers["Status"] = "Unauthorized"
118
+ headers["WWW-Authenticate"] = %(Basic realm="Web Password")
119
+ render :text => e.message, :status => "401 Unauthorized"
120
+ return
121
+ end
122
+ end
123
+ end
124
+
125
+ def session_expired(e)
126
+
127
+ Lockdown.logger.info "Access denied: #{e}"
128
+
129
+ if Lockdown::System.fetch(:logout_on_access_violation)
130
+ reset_session
131
+ end
132
+
133
+ respond_to do |format|
134
+ format.html do
135
+ store_location
136
+ session_timeout_path = Lockdown::System.fetch(:session_timeout_path) || Lockdown::System.fetch(:access_denied_path)
137
+ redirect_to session_timeout_path
138
+ return
139
+ end
140
+ format.xml do
141
+ headers["Status"] = "Unauthorized"
142
+ headers["WWW-Authenticate"] = %(Basic realm="Web Password")
143
+ render :text => e.message, :status => "401 Unauthorized"
144
+ return
145
+ end
146
+ end
147
+ end
148
+
149
+ def path_from_hash(hash)
150
+ hash[:controller].to_s + "/" + hash[:action].to_s
151
+ end
152
+
153
+ def remote_url?(domain = nil)
154
+ return false if domain.nil? || domain.strip.length == 0
155
+ request.host.downcase != domain.downcase
156
+ end
157
+
158
+ def redirect_back_or_default(default)
159
+ if session[:prevpage].nil? || session[:prevpage].blank?
160
+ redirect_to(default)
161
+ else
162
+ redirect_to(session[:prevpage])
163
+ end
164
+ end
165
+
166
+ # Called from current_user. Now, attempt to login by
167
+ # basic authentication information.
168
+ def login_from_basic_auth?
169
+ username, passwd = get_auth_data
170
+ if username && passwd
171
+ set_session_user ::User.authenticate(username, passwd)
172
+ end
173
+ end
174
+
175
+ @@http_auth_headers = %w(X-HTTP_AUTHORIZATION HTTP_AUTHORIZATION Authorization)
176
+ # gets BASIC auth info
177
+ def get_auth_data
178
+ auth_key = @@http_auth_headers.detect { |h| request.env.has_key?(h) }
179
+ auth_data = request.env[auth_key].to_s.split unless auth_key.blank?
180
+ return auth_data && auth_data[0] == 'Basic' ? Base64.decode64(auth_data[1]).split(':')[0..1] : [nil, nil]
181
+ end
182
+ end # Lock
183
+ end # Controller
184
+ end # Rails
185
+ end # Frameworks
186
+ end # Lockdown
187
+
@@ -0,0 +1,50 @@
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] : :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::System.fetch(:link_separator) )
46
+ end
47
+ end # View
48
+ end # Rails
49
+ end # Frameworks
50
+ end # Lockdown
@@ -0,0 +1,114 @@
1
+ require File.join(File.dirname(__FILE__), "rails", "controller")
2
+ require File.join(File.dirname(__FILE__), "rails", "view")
3
+
4
+ module Lockdown
5
+ module Frameworks
6
+ module Rails
7
+ class << self
8
+ def use_me?
9
+ Object.const_defined?("ActionController") && ActionController.const_defined?("Base")
10
+ end
11
+
12
+ def included(mod)
13
+ mod.extend Lockdown::Frameworks::Rails::Environment
14
+ mixin
15
+ end
16
+
17
+ def mixin
18
+ mixin_controller
19
+
20
+ Lockdown.view_helper.class_eval do
21
+ include Lockdown::Frameworks::Rails::View
22
+ end
23
+
24
+ Lockdown.system.class_eval do
25
+ extend Lockdown::Frameworks::Rails::System
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, :check_model_authorization)
38
+
39
+ klass.before_filter do |c|
40
+ c.set_current_user
41
+ c.configure_lockdown
42
+ c.check_request_authorization
43
+ c.check_model_authorization
44
+ end
45
+
46
+ klass.filter_parameter_logging :password, :password_confirmation
47
+
48
+ klass.rescue_from SecurityError, :with => proc{|e| ld_access_denied(e)}
49
+ klass.rescue_from Lockdown::Frameworks::Rails::Controller::Lock::LockdownSessionExpired, :with => proc{|e| session_expired(e)}
50
+ end
51
+ end # class block
52
+
53
+ module Environment
54
+
55
+ def project_root
56
+ ::RAILS_ROOT
57
+ end
58
+
59
+ def init_file
60
+ "#{project_root}/lib/lockdown/init.rb"
61
+ end
62
+
63
+ def view_helper
64
+ ::ActionView::Base
65
+ end
66
+
67
+ # cache_classes is true in production and testing, need to
68
+ # modify the ApplicationController
69
+ def controller_parent
70
+ if caching?
71
+ ApplicationController
72
+ else
73
+ ActionController::Base
74
+ end
75
+ end
76
+
77
+ def caching?
78
+ ::Rails.configuration.cache_classes
79
+ end
80
+
81
+ # cache_classes is true in production and testing, need to
82
+ # do an instance eval instead
83
+ def add_controller_method(code)
84
+ Lockdown.controller_parent.class_eval code, __FILE__,__LINE__ +1
85
+ end
86
+
87
+ def controller_class_name(str)
88
+ str = "#{str}Controller"
89
+ if str.include?("__")
90
+ str.split("__").collect{|p| Lockdown.camelize(p)}.join("::")
91
+ else
92
+ Lockdown.camelize(str)
93
+ end
94
+ end
95
+
96
+ def fetch_controller_class(str)
97
+ eval("::#{controller_class_name(str)}")
98
+ end
99
+ end
100
+
101
+ module System
102
+ include Lockdown::Frameworks::Rails::Controller
103
+
104
+ def skip_sync?
105
+ Lockdown.system.fetch(:skip_db_sync_in).include?(framework_environment)
106
+ end
107
+
108
+ def framework_environment
109
+ ::Rails.env
110
+ end
111
+ end # System
112
+ end # Rails
113
+ end # Frameworks
114
+ end # Lockdown