base_auth 0.2.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.
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 ] )