base_auth 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +1 -0
  2. data/README.rdoc +130 -0
  3. data/Rakefile +52 -0
  4. data/VERSION +1 -0
  5. data/base_auth.gemspec +118 -0
  6. data/init.rb +1 -0
  7. data/lib/base_auth.rb +175 -0
  8. data/tasks/base_auth_tasks.rake +4 -0
  9. data/test/helper.rb +16 -0
  10. data/test/models/user.rb +3 -0
  11. data/test/rails_app/README +243 -0
  12. data/test/rails_app/Rakefile +10 -0
  13. data/test/rails_app/app/controllers/application_controller.rb +10 -0
  14. data/test/rails_app/app/helpers/application_helper.rb +3 -0
  15. data/test/rails_app/config/boot.rb +110 -0
  16. data/test/rails_app/config/database.yml +22 -0
  17. data/test/rails_app/config/environment.rb +41 -0
  18. data/test/rails_app/config/environments/development.rb +17 -0
  19. data/test/rails_app/config/environments/production.rb +28 -0
  20. data/test/rails_app/config/environments/test.rb +28 -0
  21. data/test/rails_app/config/initializers/backtrace_silencers.rb +7 -0
  22. data/test/rails_app/config/initializers/inflections.rb +10 -0
  23. data/test/rails_app/config/initializers/mime_types.rb +5 -0
  24. data/test/rails_app/config/initializers/new_rails_defaults.rb +21 -0
  25. data/test/rails_app/config/initializers/session_store.rb +15 -0
  26. data/test/rails_app/config/locales/en.yml +5 -0
  27. data/test/rails_app/config/routes.rb +43 -0
  28. data/test/rails_app/db/seeds.rb +7 -0
  29. data/test/rails_app/db/test.sqlite3 +0 -0
  30. data/test/rails_app/doc/README_FOR_APP +2 -0
  31. data/test/rails_app/log/development.log +0 -0
  32. data/test/rails_app/log/production.log +0 -0
  33. data/test/rails_app/log/server.log +0 -0
  34. data/test/rails_app/log/test.log +34 -0
  35. data/test/rails_app/public/404.html +30 -0
  36. data/test/rails_app/public/422.html +30 -0
  37. data/test/rails_app/public/500.html +30 -0
  38. data/test/rails_app/public/favicon.ico +0 -0
  39. data/test/rails_app/public/images/rails.png +0 -0
  40. data/test/rails_app/public/index.html +275 -0
  41. data/test/rails_app/public/javascripts/application.js +2 -0
  42. data/test/rails_app/public/javascripts/controls.js +963 -0
  43. data/test/rails_app/public/javascripts/dragdrop.js +973 -0
  44. data/test/rails_app/public/javascripts/effects.js +1128 -0
  45. data/test/rails_app/public/javascripts/prototype.js +4320 -0
  46. data/test/rails_app/public/robots.txt +5 -0
  47. data/test/rails_app/script/about +4 -0
  48. data/test/rails_app/script/console +3 -0
  49. data/test/rails_app/script/dbconsole +3 -0
  50. data/test/rails_app/script/destroy +3 -0
  51. data/test/rails_app/script/generate +3 -0
  52. data/test/rails_app/script/performance/benchmarker +3 -0
  53. data/test/rails_app/script/performance/profiler +3 -0
  54. data/test/rails_app/script/plugin +3 -0
  55. data/test/rails_app/script/runner +3 -0
  56. data/test/rails_app/script/server +3 -0
  57. data/test/rails_app/test/performance/browsing_test.rb +9 -0
  58. data/test/rails_app/test/test_helper.rb +38 -0
  59. data/test/schema.rb +5 -0
  60. data/test/test_base_auth.rb +20 -0
  61. metadata +140 -0
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ /pkg
data/README.rdoc ADDED
@@ -0,0 +1,130 @@
1
+ = BaseAuth
2
+
3
+ == 0.2 beta
4
+
5
+ === Best Authorization System Ever
6
+
7
+ == Installation
8
+
9
+ Install it as you would any other plugin from github, i.e.
10
+ script/plugin install git://github.com/aenima/base-auth.git
11
+
12
+ == Authorization by Controller
13
+
14
+ === Key concept
15
+
16
+ This plugin makes 2 assumptions:
17
+
18
+ - you have a current_user method in your controller which returns the currently signed in user which you want to check authorization for.
19
+ - you check authorization using instance methods of your user objects
20
+
21
+ This worked really well for me so far, so there's a slight chance it will also work for you.
22
+
23
+ === Using as a before filter
24
+
25
+ Simple example:
26
+
27
+ class ArticleController < ApplicationController
28
+ deny :user => :is_guest?
29
+ end
30
+
31
+ The above example will deny all guest access (will call current_user.is_guest? method to determine if the user is a guest or not). If you want to allow guests to list articles, but nothing more, use:
32
+
33
+ allow :index, :user => :is_guest?
34
+
35
+ or
36
+
37
+ allow :only => :index, :user => :is_guest?
38
+
39
+ if you want to allow every user who is guest OR admin, you'd go:
40
+
41
+ allow :index, :user => [ :is_guest?, :is_admin? ]
42
+
43
+ if you give an Array as :user value, at least one condition has to be met. For more sophisticated conditions you can pass a string, which will be instance_eval'd:
44
+
45
+ allow :index, :user => 'is_guest? or ( is_admin? and is_moderator? )'
46
+
47
+ Still, there are cases when you want to check something in the controller.
48
+ In that case you can use :exec param instead of :user like this:
49
+
50
+ class ArticleController < ApplicationController
51
+ allow :exec => :check_auth
52
+
53
+ def check_auth
54
+ session[:allowed] == 'yes'
55
+ end
56
+ end
57
+
58
+ You can also pass a string, which will be eval'd, or a Proc, which will be called.
59
+
60
+ If you pass a method which accepts arguments, an object will be passed to it as a parameter. Which obejct? By default an instance variable named after singluarized controller name. So:
61
+
62
+ class ArticleController < ApplicationController
63
+ allow :edit, :update, :user => :owns?
64
+ end
65
+
66
+ Will call current_user.owns?( @article ) to check for permission. You can override this by passing :object argument, which can be a Symbol (naming instance variable to be used) or the object itself.
67
+
68
+ By default an Authorization::PermisionDenied exception will be raised. You can also use :method parameter to specify which method should be called instead or :redirect_to to redirect user instead.
69
+
70
+ With :message parameter you can pass a message that will be stored in exception.
71
+
72
+
73
+ === Using in actions
74
+
75
+ In actions you can use allow!, deny!, allow? and deny? methods. The ones with '!' will raise an exception, while the ones with '?' will only return true or false:
76
+
77
+ def destroy
78
+ allow! :user => :owns?
79
+ @article.destroy
80
+ end
81
+
82
+
83
+ === Using in views
84
+
85
+ You can use allow and deny methods in your views and pass them a block to execute:
86
+
87
+ <% allow :user => :is_admin? do %>
88
+ Only admins can see that!
89
+ <% end>
90
+
91
+ <% deny :user => :is_guest? do %>
92
+ You can't see it if you're a guest.
93
+ <% end %>
94
+
95
+
96
+ == Authorization by model
97
+
98
+ Since version 0.2 the code has been modularized a bit and you can also authorize by model (single instance for now). It's usage is dead easy and straightforward. You just have to implement authorize instance method in a given model (as a parameter it takes user to authorize against). You don't even have to do it if you're satisfied with default implementation (supplied with plugin):
99
+
100
+ def authorize(user)
101
+ user.id == self.user_id
102
+ end
103
+
104
+ This method is used by authorize! instance method which works exactly like allow! - i.e. does nothing (returns the model) if authorized and throws Authorization::PermissionDenied if not authorized:
105
+
106
+ @item = ItemModel.find(35).authorize!(current_user) #throws an exception if current_user is not authorized
107
+
108
+
109
+ == Notes
110
+
111
+ If you're using rails older than 2.0 rescuing exceptions can be a pain. For rails 1.2 I recommend using exceptional plugin:
112
+
113
+ http://agilewebdevelopment.com/plugins/exceptional
114
+
115
+
116
+ == ToDo
117
+
118
+ - add support for :if parameted (should behave like the one in validations)
119
+ - make it possible to configure default behavior (so you don't have to pass :method parameter everywhere if you always want to call a method instead of raising exception)
120
+ - better documentation
121
+ - model authorization: configuration of authorize! method to allow for redirection etc. (now it can only throw exceptions)
122
+ - model authorization: authorize_for!(:actionname), i.e. let user define and use per-action authorization for a given model (different auth checks for different actions), by authorize_for_actionname methods in model
123
+ - refactor the code structure (divide into three files)
124
+
125
+
126
+ == Credits & Licensing
127
+
128
+ Copyright (c) by Robert Nasiadek at <robert@aenima.pl>. Maintenance and small modifications (model-based auth) by Tomasz Stachewicz <tomasz.stachewicz@aenima.pl>. Distributed under MIT license.
129
+
130
+ You can find more cool stuff by Aenima on our Github (http://github.com/aenima) and our blog (http://blog.aenima.pl/).
data/Rakefile ADDED
@@ -0,0 +1,52 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "base_auth"
8
+ gem.summary = %Q{A simple and elegant solution to authorization suitable for most small and medium-sized projects.}
9
+ gem.description = %Q{A simple and elegant solution to authorization suitable for most small and medium-sized projects.}
10
+ gem.email = "drogus@gmail.com"
11
+ gem.homepage = "http://github.com/drogus/base_auth"
12
+ gem.authors = ["Piotr Sarnacki"]
13
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ require 'rake/testtask'
21
+ Rake::TestTask.new(:test) do |test|
22
+ test.libs << 'lib' << 'test'
23
+ test.pattern = 'test/**/test_*.rb'
24
+ test.verbose = true
25
+ end
26
+
27
+ begin
28
+ require 'rcov/rcovtask'
29
+ Rcov::RcovTask.new do |test|
30
+ test.libs << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+ rescue LoadError
35
+ task :rcov do
36
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
37
+ end
38
+ end
39
+
40
+ task :test => :check_dependencies
41
+
42
+ task :default => :test
43
+
44
+ require 'rake/rdoctask'
45
+ Rake::RDocTask.new do |rdoc|
46
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
47
+
48
+ rdoc.rdoc_dir = 'rdoc'
49
+ rdoc.title = "base_auth #{version}"
50
+ rdoc.rdoc_files.include('README*')
51
+ rdoc.rdoc_files.include('lib/**/*.rb')
52
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.1
data/base_auth.gemspec ADDED
@@ -0,0 +1,118 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{base_auth}
8
+ s.version = "0.2.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Piotr Sarnacki"]
12
+ s.date = %q{2010-03-15}
13
+ s.description = %q{A simple and elegant solution to authorization suitable for most small and medium-sized projects.}
14
+ s.email = %q{drogus@gmail.com}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION",
23
+ "base_auth.gemspec",
24
+ "init.rb",
25
+ "lib/base_auth.rb",
26
+ "tasks/base_auth_tasks.rake",
27
+ "test/helper.rb",
28
+ "test/models/user.rb",
29
+ "test/rails_app/README",
30
+ "test/rails_app/Rakefile",
31
+ "test/rails_app/app/controllers/application_controller.rb",
32
+ "test/rails_app/app/helpers/application_helper.rb",
33
+ "test/rails_app/config/boot.rb",
34
+ "test/rails_app/config/database.yml",
35
+ "test/rails_app/config/environment.rb",
36
+ "test/rails_app/config/environments/development.rb",
37
+ "test/rails_app/config/environments/production.rb",
38
+ "test/rails_app/config/environments/test.rb",
39
+ "test/rails_app/config/initializers/backtrace_silencers.rb",
40
+ "test/rails_app/config/initializers/inflections.rb",
41
+ "test/rails_app/config/initializers/mime_types.rb",
42
+ "test/rails_app/config/initializers/new_rails_defaults.rb",
43
+ "test/rails_app/config/initializers/session_store.rb",
44
+ "test/rails_app/config/locales/en.yml",
45
+ "test/rails_app/config/routes.rb",
46
+ "test/rails_app/db/seeds.rb",
47
+ "test/rails_app/db/test.sqlite3",
48
+ "test/rails_app/doc/README_FOR_APP",
49
+ "test/rails_app/log/development.log",
50
+ "test/rails_app/log/production.log",
51
+ "test/rails_app/log/server.log",
52
+ "test/rails_app/log/test.log",
53
+ "test/rails_app/public/404.html",
54
+ "test/rails_app/public/422.html",
55
+ "test/rails_app/public/500.html",
56
+ "test/rails_app/public/favicon.ico",
57
+ "test/rails_app/public/images/rails.png",
58
+ "test/rails_app/public/index.html",
59
+ "test/rails_app/public/javascripts/application.js",
60
+ "test/rails_app/public/javascripts/controls.js",
61
+ "test/rails_app/public/javascripts/dragdrop.js",
62
+ "test/rails_app/public/javascripts/effects.js",
63
+ "test/rails_app/public/javascripts/prototype.js",
64
+ "test/rails_app/public/robots.txt",
65
+ "test/rails_app/script/about",
66
+ "test/rails_app/script/console",
67
+ "test/rails_app/script/dbconsole",
68
+ "test/rails_app/script/destroy",
69
+ "test/rails_app/script/generate",
70
+ "test/rails_app/script/performance/benchmarker",
71
+ "test/rails_app/script/performance/profiler",
72
+ "test/rails_app/script/plugin",
73
+ "test/rails_app/script/runner",
74
+ "test/rails_app/script/server",
75
+ "test/rails_app/test/performance/browsing_test.rb",
76
+ "test/rails_app/test/test_helper.rb",
77
+ "test/schema.rb",
78
+ "test/test_base_auth.rb"
79
+ ]
80
+ s.homepage = %q{http://github.com/drogus/base_auth}
81
+ s.rdoc_options = ["--charset=UTF-8"]
82
+ s.require_paths = ["lib"]
83
+ s.rubygems_version = %q{1.3.6}
84
+ s.summary = %q{A simple and elegant solution to authorization suitable for most small and medium-sized projects.}
85
+ s.test_files = [
86
+ "test/helper.rb",
87
+ "test/models/user.rb",
88
+ "test/rails_app/app/controllers/application_controller.rb",
89
+ "test/rails_app/app/helpers/application_helper.rb",
90
+ "test/rails_app/config/boot.rb",
91
+ "test/rails_app/config/environment.rb",
92
+ "test/rails_app/config/environments/development.rb",
93
+ "test/rails_app/config/environments/production.rb",
94
+ "test/rails_app/config/environments/test.rb",
95
+ "test/rails_app/config/initializers/backtrace_silencers.rb",
96
+ "test/rails_app/config/initializers/inflections.rb",
97
+ "test/rails_app/config/initializers/mime_types.rb",
98
+ "test/rails_app/config/initializers/new_rails_defaults.rb",
99
+ "test/rails_app/config/initializers/session_store.rb",
100
+ "test/rails_app/config/routes.rb",
101
+ "test/rails_app/db/seeds.rb",
102
+ "test/rails_app/test/performance/browsing_test.rb",
103
+ "test/rails_app/test/test_helper.rb",
104
+ "test/schema.rb",
105
+ "test/test_base_auth.rb"
106
+ ]
107
+
108
+ if s.respond_to? :specification_version then
109
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
110
+ s.specification_version = 3
111
+
112
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
113
+ else
114
+ end
115
+ else
116
+ end
117
+ end
118
+
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'base_auth'
data/lib/base_auth.rb ADDED
@@ -0,0 +1,175 @@
1
+ # BaseAuth
2
+
3
+ module Authorization
4
+
5
+ DEFAULT_PERMISSION_DENIED_MESSAGE = 'You have no permissions to access this page.'
6
+
7
+ def self.included( base )
8
+ base.extend( ClassMethods )
9
+ base.send( :include, InstanceMethods )
10
+ end
11
+
12
+ class PermissionDenied < Exception
13
+ attr :message
14
+ def initialize( message = nil )
15
+ @message = message || DEFAULT_PERMISSION_DENIED_MESSAGE
16
+ end
17
+ def clean_message
18
+ @message
19
+ end
20
+ def to_s
21
+ @message
22
+ end
23
+ end
24
+
25
+ module InstanceMethods
26
+
27
+ def eval_authorization_conditions( conditions, user_for_auth )
28
+ exec = conditions[:exec]
29
+ if exec
30
+ case exec.class.name
31
+ when 'Symbol':
32
+ send( exec )
33
+ when 'String':
34
+ eval( exec )
35
+ when 'Proc':
36
+ exec.call
37
+ else
38
+ raise ArgumentError( ":exec doesn't accept values of class #{exec.class.name}" )
39
+ end
40
+ elsif conditions[:user]
41
+ if conditions[:user].is_a?( String )
42
+ return user_for_auth.instance_eval conditions[:user]
43
+ end
44
+ o = conditions[:object] || controller_name.singularize.to_sym
45
+ object = o.is_a?( Symbol ) ? eval("@#{o}") : o
46
+
47
+ methods = conditions[:user]
48
+ methods = [ methods ] if not methods.is_a?( Array )
49
+
50
+ for method in methods
51
+ if user_for_auth.respond_to?( method ) and
52
+ user_for_auth.method( method ).arity == 0
53
+ return true if user_for_auth.send( method )
54
+ else
55
+ return true if user_for_auth.send( method, object )
56
+ end
57
+ end
58
+ return false
59
+ end
60
+ end
61
+
62
+ def allow?( conditions )
63
+ eval_authorization_conditions( conditions )
64
+ end
65
+
66
+ def deny?( conditions )
67
+ !eval_authorization_conditions( conditions )
68
+ end
69
+
70
+ def allow!( conditions )
71
+ invoke_permission_denied_action( conditions ) unless allow?( conditions )
72
+ end
73
+
74
+ def deny!( conditions )
75
+ invoke_permission_denied_action( conditions ) unless deny?( conditions )
76
+ end
77
+
78
+ def allow( conditions )
79
+ if allow?( conditions )
80
+ yield
81
+ end
82
+ end
83
+
84
+ def deny( conditions )
85
+ if deny?( conditions )
86
+ yield
87
+ end
88
+ end
89
+
90
+ def invoke_permission_denied_action( config )
91
+ if config[:redirect_to]
92
+ redirect_to config[:redirect_to]
93
+ elsif config[:method]
94
+ send( config[:method] )
95
+ else
96
+ raise Authorization::PermissionDenied.new( config[:message] )
97
+ end
98
+ false
99
+ end
100
+
101
+ end
102
+
103
+ module ClassMethods
104
+
105
+ def authorization_filter( action, attrs )
106
+ config = { }
107
+ config.update( attrs.pop ) if attrs.last.is_a?( Hash )
108
+
109
+ opts = {}
110
+ opts[:only] = config[:only]
111
+ opts[:only] ||= attrs unless attrs.blank?
112
+ opts[:except] = config[:except]
113
+
114
+ if config[:if]
115
+
116
+ end
117
+
118
+ before_filter opts do |controller|
119
+ if action == :allow
120
+ controller.allow! config
121
+ else
122
+ controller.deny! config
123
+ end
124
+ end
125
+ end
126
+
127
+ def allow( *attrs )
128
+ authorization_filter :allow, attrs
129
+ end
130
+
131
+ def deny( *attrs )
132
+ authorization_filter :deny, attrs
133
+ end
134
+ end
135
+
136
+ end
137
+
138
+
139
+ module ControllerAuthorization
140
+
141
+ def self.included( base )
142
+ base.send( :include, Authorization )
143
+ base.send( :include, InstanceMethods )
144
+ end
145
+
146
+ module InstanceMethods
147
+ def eval_authorization_conditions( conditions )
148
+ super( conditions, current_user )
149
+ end
150
+ end
151
+ end
152
+
153
+ module ModelAuthorization
154
+
155
+ def self.included( base )
156
+ base.send( :include, Authorization )
157
+ base.send( :include, InstanceMethods )
158
+ end
159
+
160
+ module InstanceMethods
161
+ def authorize!(for_user)
162
+ invoke_permission_denied_action( {} ) unless self.authorize(for_user)
163
+ self
164
+ end
165
+
166
+ #re-implement this in your model if you check authorization in a different way
167
+ def authorize(user)
168
+ user.id == self.user_id
169
+ end
170
+ end
171
+ end
172
+
173
+ ActiveRecord::Base.send( :include, ModelAuthorization )
174
+ ActionController::Base.send( :include, ControllerAuthorization )
175
+ ActionController::Base.send( :helper_method, [ :allow, :deny ] )