checkin 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "jeweler", "~> 1.8.3"
5
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,19 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ git (1.2.5)
5
+ jeweler (1.8.3)
6
+ bundler (~> 1.0)
7
+ git (>= 1.2.5)
8
+ rake
9
+ rdoc
10
+ json (1.7.1)
11
+ rake (0.9.2.2)
12
+ rdoc (3.12)
13
+ json (~> 1.4)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ jeweler (~> 1.8.3)
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 mcasimir
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # Checkin
2
+
3
+ **Checkin** is an authorization gem for Ruby on Rails
4
+
5
+ ## Installation
6
+
7
+ Put this in your Gemfile
8
+
9
+ gem 'checkin'
10
+
11
+ and update your bundle
12
+
13
+ bundle install
14
+
15
+ ## Usage
16
+
17
+ Create a **subject class** in your load path
18
+
19
+ _ex._
20
+
21
+ ``` rb
22
+ class UserSubject < Checkin::Subject
23
+
24
+ role :guest, :alias => :anonymous do
25
+ !subject_model
26
+ end
27
+
28
+ role :logged_in, :alias => [:connected] do
29
+ !!subject_model
30
+ end
31
+
32
+ role :owner, :require => [:logged_in, :author], :method => :own do |object|
33
+ object && object.respond_to?(:author) && ( subject_model == object.author )
34
+ end
35
+
36
+ role :author, :require => :logged_in do
37
+ subject_model.has_role?(:contributor) || subject_model.has_role?(:author)
38
+ end
39
+
40
+ role :editor, :require => :logged_in do
41
+ subject_model.has_role?(:editor)
42
+ end
43
+
44
+ role :administrator, :require => :logged_in, :alias => :admin do
45
+ subject_model.has_role?(:administrator)
46
+ end
47
+
48
+ role :mantainer, :require => :logged_in do
49
+ subject_model.has_role?(:mantainer)
50
+ end
51
+
52
+ role :staff, :require => :logged_in do
53
+ subject_model.has_role?(:administrator) ||
54
+ subject_model.has_role?(:mantainer) ||
55
+ subject_model.has_role?(:editor) ||
56
+ subject_model.has_role?(:author) ||
57
+ subject_model.has_role?(:contributor) ||
58
+ subject_model.has_role?(:photographer)
59
+ end
60
+
61
+ role :tester, :require => :logged_in do
62
+ subject_model.has_role?(:tester)
63
+ end
64
+
65
+ role :nexta_supervisor, :require => :logged_in do
66
+ subject_model.has_role?(:nexta)
67
+ end
68
+
69
+ role :self, :require => :logged_in do |object|
70
+ object && object.is_a?(User) && subject_model == object
71
+ end
72
+
73
+ #
74
+ # Permissions
75
+ #
76
+
77
+ scope :site do
78
+ permissions :for => [:messages, :replies] do
79
+ allow :logged_in
80
+ allow :guests, :to => [:show, :index]
81
+ deny :guests
82
+ end
83
+ permissions :for => :network do
84
+ allow [:administrators, :nexta_supervisors], :to => [:see]
85
+ deny
86
+ end
87
+ end
88
+
89
+ # Admin
90
+ scope :admin do
91
+ permissions do
92
+ allow :administrators, :mantainers
93
+ deny
94
+ end
95
+ end
96
+
97
+ scope :api do
98
+ allow :staff
99
+ deny
100
+ end
101
+
102
+ # Mantainer
103
+ scope :mantainer do
104
+ permissions do
105
+ allow :mantainers
106
+ deny
107
+ end
108
+ end
109
+
110
+ # Staff
111
+ scope :staff do
112
+ permissions do
113
+ allow :administrators, :mantainers
114
+ allow :editors, :to => [:edit, :update]
115
+ allow :authors, :to => [:new, :create]
116
+ allow :owners, :to => [:edit, :update]
117
+ deny
118
+ end
119
+
120
+ permissions :for => :guides do
121
+ allow :administrators, :mantainers
122
+ allow :logged_in, :to => [:index, :show]
123
+ deny
124
+ end
125
+
126
+ permissions :for => :drafts do
127
+ allow :authors, :to => :submit
128
+ end
129
+
130
+ permissions_to_set :published do
131
+ allow :editors, :administrators, :mantainers
132
+ deny
133
+ end
134
+
135
+ end
136
+ end
137
+ ```
138
+
139
+ Use in controller
140
+
141
+ _ex._
142
+
143
+ ``` rb
144
+ class ApplicationController < ActionController::Base
145
+ rescue_from Checkin::AccessDenied, :with => :rescue_access_denied
146
+
147
+ protect_from_forgery
148
+ checkin(:scope => :admin)
149
+
150
+ protected
151
+
152
+ def rescue_access_denied
153
+ if subject.guest?
154
+ redirect_to new_user_session_path, :notice => "Accedi per completare l'operazione"
155
+ else
156
+ render :text => "Not Authorized", :status => 403
157
+ end
158
+ end
159
+
160
+ end
161
+ ```
162
+
163
+
164
+
165
+
166
+ ---
167
+
168
+ Copyright (c) 2012 mcasimir
169
+
170
+ Permission is hereby granted, free of charge, to any person obtaining
171
+ a copy of this software and associated documentation files (the
172
+ "Software"), to deal in the Software without restriction, including
173
+ without limitation the rights to use, copy, modify, merge, publish,
174
+ distribute, sublicense, and/or sell copies of the Software, and to
175
+ permit persons to whom the Software is furnished to do so, subject to
176
+ the following conditions:
177
+
178
+ The above copyright notice and this permission notice shall be
179
+ included in all copies or substantial portions of the Software.
180
+
181
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
182
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
183
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
184
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
185
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
186
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
187
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
188
+
data/Rakefile ADDED
@@ -0,0 +1,26 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'jeweler'
15
+ Jeweler::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
17
+ gem.name = "checkin"
18
+ gem.homepage = "http://github.com/mcasimir/checkin"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Checkin is an authorization gem for Ruby on Rails}
21
+ gem.description = %Q{Checkin is an authorization gem for Ruby on Rails}
22
+ gem.email = "maurizio.cas@gmail.com"
23
+ gem.authors = ["mcasimir"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Jeweler::RubygemsDotOrgTasks.new
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.3
data/checkin.gemspec ADDED
@@ -0,0 +1,57 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "checkin"
8
+ s.version = "0.4.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["mcasimir"]
12
+ s.date = "2012-05-07"
13
+ s.description = "Checkin is an authorization gem for Ruby on Rails"
14
+ s.email = "maurizio.cas@gmail.com"
15
+ s.extra_rdoc_files = [
16
+ "LICENSE.txt",
17
+ "README.md"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ "Gemfile",
22
+ "Gemfile.lock",
23
+ "LICENSE.txt",
24
+ "README.md",
25
+ "Rakefile",
26
+ "VERSION",
27
+ "checkin.gemspec",
28
+ "lib/checkin.rb",
29
+ "lib/checkin/access_denied.rb",
30
+ "lib/checkin/dsl/permissions.rb",
31
+ "lib/checkin/dsl/roles.rb",
32
+ "lib/checkin/filters.rb",
33
+ "lib/checkin/role.rb",
34
+ "lib/checkin/rule.rb",
35
+ "lib/checkin/subject.rb",
36
+ "test/helper.rb",
37
+ "test/test_checkin.rb"
38
+ ]
39
+ s.homepage = "http://github.com/mcasimir/checkin"
40
+ s.licenses = ["MIT"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = "1.8.24"
43
+ s.summary = "Checkin is an authorization gem for Ruby on Rails"
44
+
45
+ if s.respond_to? :specification_version then
46
+ s.specification_version = 3
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_development_dependency(%q<jeweler>, ["~> 1.8.3"])
50
+ else
51
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
52
+ end
53
+ else
54
+ s.add_dependency(%q<jeweler>, ["~> 1.8.3"])
55
+ end
56
+ end
57
+
@@ -0,0 +1,46 @@
1
+ # Author:: Maurizio Casimirri (mailto:maurizio.cas@gmail.com)
2
+ # Copyright:: Copyright (c) 2012 Maurizio Casimirri
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module Checkin
24
+ class AccessDenied < StandardError
25
+ attr_reader :subject, :action, :resource, :object
26
+
27
+ def initialize(subject, action, object_or_resource, options = {})
28
+
29
+ @message = options[:message]
30
+ @subject = subject
31
+ @action = action
32
+ @object = ( object_or_resource.is_a?(Symbol) || object_or_resource.is_a?(String)) ? nil : object_or_resource
33
+ @resource = object ? object.class.name.demodulize.underscore.to_sym : "#{object_or_resource}".singularize.to_sym
34
+
35
+ @default_message = I18n.t(:"unauthorized.default", :default => "You are not authorized to access this page.")
36
+ end
37
+
38
+ def scope
39
+ @subject.scope if @subject
40
+ end
41
+
42
+ def to_s
43
+ @message || @default_message
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,167 @@
1
+ # Author:: Maurizio Casimirri (mailto:maurizio.cas@gmail.com)
2
+ # Copyright:: Copyright (c) 2012 Maurizio Casimirri
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'checkin/rule'
24
+
25
+ #
26
+ # Permissions DSL
27
+ #
28
+
29
+ module Checkin
30
+ module Dsl
31
+ module Permissions
32
+ extend ActiveSupport::Concern
33
+
34
+ included do
35
+ attr_writer :explain
36
+ end
37
+
38
+ module ClassMethods
39
+
40
+ def rules
41
+ @rules ||= []
42
+ end
43
+
44
+ def add_rule_block(rules)
45
+ @rules ||= []
46
+ @rules = rules + @rules
47
+ end
48
+
49
+ def attribute_rules
50
+ @attribute_rules ||= []
51
+ end
52
+
53
+ def add_attribute_rules_block(rules)
54
+ @attribute_rules ||= []
55
+ @attribute_rules = rules + @attribute_rules
56
+ end
57
+
58
+ def scope(scope, &block)
59
+ _current_options[:scope] = scope
60
+ yield
61
+ _current_options.delete(:scope)
62
+ end
63
+
64
+ def permissions(*args)
65
+ _current_options[:mode] = :cascade
66
+ opts = args.extract_options!
67
+ _current_options[:resources] = [opts[:for]].flatten.compact.uniq.map {|r| "#{r}".singularize.to_sym}
68
+ yield
69
+ add_rule_block(_current_rule_block)
70
+ _reset_current_options
71
+ _reset_current_rule_block
72
+ end
73
+
74
+ def permissions_to_set(*args)
75
+ _current_options[:mode] = :attributes
76
+ opts = args.extract_options!
77
+ resources_options = opts[:on]
78
+ _current_options[:resources] = [resources_options].flatten.compact.uniq.map {|r| "#{r}".singularize.to_sym}
79
+ _current_options[:attributes] = args.flatten
80
+ yield
81
+ add_attribute_rules_block(_current_attributes_block)
82
+ _reset_current_options
83
+ _reset_current_attributes_block
84
+ end
85
+
86
+ def allow(*args)
87
+ if _current_options[:mode] == :cascade
88
+ _add_rule_to_current_rule_block(:allow, *args)
89
+ elsif _current_options[:mode] == :attributes
90
+ _add_rule_to_current_attributes_block(:allow, *args)
91
+ end
92
+ end
93
+
94
+ def deny(*args)
95
+ if _current_options[:mode] == :cascade
96
+ _add_rule_to_current_rule_block(:deny, *args)
97
+ elsif _current_options[:mode] == :attributes
98
+ _add_rule_to_current_attributes_block(:deny, *args)
99
+ end
100
+ end
101
+
102
+ def _current_options
103
+ @_current_options ||= ::ActiveSupport::HashWithIndifferentAccess.new
104
+ end
105
+
106
+ def _reset_current_options
107
+ scope = _current_options[:scope]
108
+ @_current_options = ::ActiveSupport::HashWithIndifferentAccess.new
109
+ @_current_options[:scope] = scope
110
+ @_current_options
111
+ end
112
+
113
+ def _current_rule_block
114
+ @_current_rule_block ||= []
115
+ end
116
+
117
+ def _reset_current_rule_block
118
+ @_current_rule_block = []
119
+ end
120
+
121
+ def _add_rule_to_current_rule_block(kind, *args)
122
+ opts = args.extract_options!
123
+ actions = _actions_from_options(args, opts)
124
+ roles = _roles_from_options(args, opts)
125
+
126
+ rule = ::Checkin::Rule.new(kind, :resources => _current_options[:resources], :roles => roles, :actions_or_attributes => actions, :scope => _current_options[:scope])
127
+
128
+ @_current_rule_block ||= []
129
+ @_current_rule_block << rule
130
+
131
+ rule
132
+ end
133
+
134
+ def _current_attributes_block
135
+ @_current_attributes_block ||= []
136
+ end
137
+
138
+ def _reset_current_attributes_block
139
+ @_current_attributes_block = []
140
+ end
141
+
142
+ def _add_rule_to_current_attributes_block(kind, *args)
143
+ opts = args.extract_options!
144
+ roles = _roles_from_options(args, opts)
145
+
146
+ rule = ::Checkin::Rule.new(kind, :resources => _current_options[:resources], :roles => roles, :actions_or_attributes => _current_options[:attributes], :scope => _current_options[:scope])
147
+
148
+ @_current_attributes_block ||= []
149
+ @_current_attributes_block << rule
150
+
151
+ rule
152
+ end
153
+
154
+ def _actions_from_options(args, opts)
155
+ [ opts[:to] ].flatten.compact.uniq.map {|a| "#{a}".to_sym}
156
+ end
157
+
158
+ def _roles_from_options(args, opts)
159
+ args.flatten.compact.uniq.map {|a| "#{a}".singularize.to_sym}
160
+ end
161
+
162
+ end
163
+
164
+ end
165
+ end
166
+ end
167
+
@@ -0,0 +1,149 @@
1
+ # Author:: Maurizio Casimirri (mailto:maurizio.cas@gmail.com)
2
+ # Copyright:: Copyright (c) 2012 Maurizio Casimirri
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'checkin/role'
24
+
25
+ module Checkin
26
+ module Dsl
27
+ module Roles
28
+ extend ActiveSupport::Concern
29
+
30
+ module ClassMethods
31
+
32
+ def role(*args, &block)
33
+ opts = args.extract_options!
34
+
35
+ if args.empty?
36
+ raise ::ArgumentError.new("wrong number of arguments")
37
+ end
38
+
39
+ role_name = args.first
40
+
41
+ if role_name.to_s =~ /^can_([A-z]_)+\?$/
42
+ raise "bad role name: '#{role_name}'. Roles can not be in the form of 'can [...]?''"
43
+ end
44
+
45
+ if role_name.to_s.singularize != role_name.to_s
46
+ raise "bad role name: '#{role_name}'. Roles can not be in the plural form"
47
+ end
48
+
49
+ role = ::Checkin::Role.new
50
+ role.name = role_name
51
+ role.dependencies = [ opts[:require] ].flatten.compact.uniq
52
+ role.check_proc = block
53
+ role.aliases = ([ opts[:alias] ] + [ opts[:aliases] ]).flatten.compact.uniq.map {|a| "#{a.to_s.gsub(/\?$/, "")}?"}
54
+ role.check_method_name = "#{(opts[:method] || role_name).to_s.gsub(/\?$/, "")}?"
55
+
56
+ self.register_role(role)
57
+ end
58
+
59
+ def roles
60
+ @roles ||= ::ActiveSupport::HashWithIndifferentAccess.new
61
+ end
62
+
63
+ def role_methods
64
+ @role_methods ||= ::ActiveSupport::HashWithIndifferentAccess.new
65
+ end
66
+
67
+ def register_role(role)
68
+
69
+ self.register_role_method(role.check_method_name, role)
70
+
71
+ role.aliases.each do |a|
72
+ self.register_role_method(a, role)
73
+ end
74
+
75
+ self.roles[role.name] = role
76
+
77
+ end
78
+
79
+ def register_role_method(meth, role)
80
+ self.role_methods[meth] = role
81
+ end
82
+
83
+ def find_role_by_method(meth)
84
+ self.role_methods[meth]
85
+ end
86
+
87
+ def find_role_by_name(name)
88
+ self.roles[name]
89
+ end
90
+
91
+ def role_names
92
+ self.roles.keys.map {|k| k.to_s}
93
+ end
94
+
95
+ def symbolized_role_names
96
+ self.roles.keys.map {|k| k.to_sym}
97
+ end
98
+
99
+ end
100
+
101
+ def roles(*args)
102
+ roles = []
103
+ self.class.roles.each do |role_key, role|
104
+ roles << "#{role_key}".to_sym if check_role(role, *args)
105
+ end
106
+ roles
107
+ end
108
+
109
+ def role?(role, *args)
110
+ !!( check_role(role, *args) if (role = find_role_by_name(name)) )
111
+ end
112
+
113
+ protected
114
+
115
+ def find_role_by_method(meth)
116
+ self.class.find_role_by_method(meth)
117
+ end
118
+
119
+ def find_role_by_name(name)
120
+ self.class.find_role_by_name(name)
121
+ end
122
+
123
+ def is_role_method?(meth)
124
+ !!find_role_by_method(meth)
125
+ end
126
+
127
+ def check_role(role, *args)
128
+
129
+ roles_chain = role.dependencies.map{|d|
130
+ find_role_by_name(d) || (raise "required role not found '#{d}' for '#{role.name}")
131
+ } + [role]
132
+
133
+ roles_chain.each do |chain_role|
134
+ check_proc = chain_role.check_proc
135
+ check_args = args.slice(0, chain_role.check_proc.arity)
136
+
137
+ if !instance_exec(*check_args, &check_proc)
138
+ return false
139
+ end
140
+
141
+ end
142
+
143
+ true
144
+ end
145
+
146
+ end
147
+ end
148
+ end
149
+
@@ -0,0 +1,78 @@
1
+ module Checkin
2
+ module Filters
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ # checkin(:subject => :user, :scope => nil, :skip_authorization => false, :object => :object, rescue_with => lambda {
7
+ # if subject.guest?
8
+ # redirect_to new_user_session_path, :notice => "Accedi per completare l'operazione"
9
+ # else
10
+ # render :text => "Not Authorized", :status => 403
11
+ # end
12
+ # })
13
+
14
+ def checkin(opts = {})
15
+ opts.symbolize_keys!
16
+ from_subject = :"#{(opts[:subject] || :user_subject)}"
17
+ subject_name = :"#{(opts[:as] || :subject)}"
18
+ subject_model = :"#{(opts[:from] || :current_user)}"
19
+ subject_class = from_subject.to_s.camelize.constantize
20
+ object_method = :"#{(opts[:object] || :fetch_object)}"
21
+ find_method = opts[:find_object]
22
+
23
+
24
+ define_method "#{subject_name}" do
25
+ if !instance_variable_get("@checkin_#{subject_name}")
26
+ instance_variable_set("@checkin_#{subject_name}", subject_class.new(self.send(subject_model), :scope => opts[:scope]))
27
+ else
28
+ instance_variable_get("@checkin_#{subject_name}")
29
+ end
30
+ end
31
+
32
+ helper_method :"#{subject_name}"
33
+
34
+ if opts[:rescue_with]
35
+ block = opts[:rescue_with]
36
+ define_method :rescue_from_checkin_access_denied, &block
37
+ rescue_from Checkin::AccessDenied, :with => :rescue_from_checkin_access_denied
38
+ end
39
+
40
+ if !opts[:skip_authorization]
41
+ define_method :"#{object_method}" do
42
+
43
+
44
+ if params[:id]
45
+ model_class = self.controller_name.singularize.camelize.constantize
46
+ singular_name = :"#{model_class.name.underscore.singularize}"
47
+ if !instance_variable_get("@#{singular_name}")
48
+ find_method = if find_method.nil?
49
+ Proc.new {|model_class, params|
50
+ model_class.find(params[:id])
51
+ }
52
+ elsif find_method.is_a?(Proc) || find_method.is_a?(Method)
53
+ find_method
54
+ elsif find_method.is_a?(Symbol) || find_method.is_a?(String)
55
+ self.method(find_method)
56
+ else
57
+ raise "'#{find_method.class.name}' is an invalid type for find method"
58
+ end
59
+ instance_variable_set("@#{singular_name}", find_method.call(model_class, params))
60
+ else
61
+ instance_variable_get("@#{singular_name}")
62
+ end
63
+ end
64
+ end
65
+
66
+ before_filter do |controller|
67
+ if Rails.env.development?
68
+ controller.send(subject_name).explain!
69
+ end
70
+ controller.send(subject_name).checkin!( :"#{controller.action_name}", (controller.send(:"#{object_method}") || {:for => :"#{controller.controller_name}"}) )
71
+ controller.send(subject_name).delete_denied_params( :"#{controller.action_name}", (controller.send(:"#{object_method}") || {:for => :"#{controller.controller_name}"}), (params[:"#{controller.controller_name.singularize}"] || {}) )
72
+ end
73
+ end
74
+ end
75
+ end
76
+
77
+ end
78
+ end
@@ -0,0 +1,26 @@
1
+ # Author:: Maurizio Casimirri (mailto:maurizio.cas@gmail.com)
2
+ # Copyright:: Copyright (c) 2012 Maurizio Casimirri
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ class Checkin::Role
24
+ attr_accessor :name, :dependencies, :check_proc, :aliases, :check_method_name
25
+ end
26
+
@@ -0,0 +1,104 @@
1
+ # Author:: Maurizio Casimirri (mailto:maurizio.cas@gmail.com)
2
+ # Copyright:: Copyright (c) 2012 Maurizio Casimirri
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ class Checkin::Rule
24
+ attr_accessor :kind, :actions_or_attributes, :roles, :resources, :scope
25
+
26
+ def initialize(kind, *args)
27
+ opts = args.extract_options!
28
+ @kind = kind
29
+ @actions_or_attributes = (opts[:actions_or_attributes] || []).map {|a| "#{a}".to_sym }
30
+ @roles = (opts[:roles] || []).map {|r| "#{r}".to_sym }
31
+ @resources = (opts[:resources] || []).map {|r| "#{r}".singularize.to_sym }
32
+ @scope = opts[:scope].present? ? opts[:scope].to_sym : nil
33
+ end
34
+
35
+ def check(subject, action_or_attribute, object_or_resource)
36
+ scope = subject.scope
37
+ action = "#{action}".to_sym
38
+
39
+ object_or_resource = object_or_resource[:for] if object_or_resource.is_a?(Hash)
40
+ object = ( object_or_resource.is_a?(Symbol) || object_or_resource.is_a?(String)) ? nil : object_or_resource
41
+ resource = object ? object.class.name.demodulize.underscore.to_sym : "#{object_or_resource}".singularize.to_sym
42
+
43
+ if affect_scope?(scope) && affect_role?(subject, object) && affect_action_or_attribute?(action_or_attribute) && affect_resource?(resource)
44
+ deny? ? :denied : :allowed
45
+ else
46
+ :skip
47
+ end
48
+ end
49
+
50
+ def to_s
51
+ formatted_roles = roles.map {|r| ":#{r}" }.join(", ") if roles.any?
52
+ formatted_actions = ":to => [" + actions_or_attributes.map {|a| ":#{a}" }.join(", ") + "]" if actions_or_attributes.any?
53
+ formatted_scope = ":scope => :#{scope}" if scope
54
+ formatted_resources = ":resources => [" + actions_or_attributes.map {|r| ":#{r}" }.join(", ") + "]" if resources.any?
55
+ args = [formatted_roles, formatted_actions, formatted_resources, formatted_scope].compact.join(", ")
56
+ "#{kind}(#{args})"
57
+ end
58
+
59
+ private
60
+
61
+ def affect_scope?(scope)
62
+ scope && ( any_scope? || @scope == scope.to_sym )
63
+ end
64
+
65
+ def affect_role?(subject, object)
66
+ args = [object].compact
67
+ any_role? || (subject.roles(*args) & @roles).any?
68
+ end
69
+
70
+ def affect_action_or_attribute?(action_or_attribute)
71
+ any_action_or_attribute? || @actions_or_attributes.include?(action_or_attribute)
72
+ end
73
+
74
+ def affect_resource?(resource)
75
+ any_resource? || @resources.include?(resource)
76
+ end
77
+
78
+ def any_role?
79
+ !!@roles.empty?
80
+ end
81
+
82
+ def any_action_or_attribute?
83
+ !!@actions_or_attributes.empty?
84
+ end
85
+
86
+ def any_scope?
87
+ @scope.blank?
88
+ end
89
+
90
+ def any_resource?
91
+ !!@resources.empty?
92
+ end
93
+
94
+ def deny?
95
+ @kind == :deny
96
+ end
97
+
98
+ def allow?
99
+ @kind == :allow
100
+ end
101
+
102
+
103
+ end
104
+
@@ -0,0 +1,137 @@
1
+ # Author:: Maurizio Casimirri (mailto:maurizio.cas@gmail.com)
2
+ # Copyright:: Copyright (c) 2012 Maurizio Casimirri
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ require 'checkin/dsl/roles'
24
+ require 'checkin/dsl/permissions'
25
+
26
+ class Checkin::Subject
27
+ include ::Checkin::Dsl::Roles
28
+ include ::Checkin::Dsl::Permissions
29
+
30
+ def initialize(subject_model, scope = {})
31
+ @subject_model = subject_model
32
+ @scope = scope[:scope]
33
+ end
34
+
35
+ def subject_model
36
+ @subject_model
37
+ end
38
+
39
+ def scope
40
+ :"#{@scope}"
41
+ end
42
+
43
+ def can?(action, object_or_resource)
44
+
45
+ if @explain
46
+ Rails.logger.info " + can?(:#{action}, #{object_or_resource})"
47
+ end
48
+
49
+ self.class.rules.each do|rule|
50
+ result = rule.check(self, :"#{action}", object_or_resource)
51
+
52
+ if @explain
53
+ Rails.logger.info [" - #{rule}".ljust(65), ":#{result}"].join(" => ")
54
+ end
55
+
56
+ case result
57
+ when :denied
58
+ return false
59
+ when :allowed
60
+ return true
61
+ else
62
+ end
63
+ end
64
+ true
65
+ end
66
+
67
+ def allowed_to_set?(attribute, on = {})
68
+
69
+ object = on[:on]
70
+ if @explain
71
+ Rails.logger.info " + allowed_to_set?(:#{attribute}, on => #{object})"
72
+ end
73
+
74
+ self.class.attribute_rules.each do|rule|
75
+ result = rule.check(self, :"#{attribute}", object)
76
+
77
+ if @explain
78
+ Rails.logger.info [" - #{rule}".ljust(65), ":#{result}"].join(" => ")
79
+ end
80
+
81
+ case result
82
+ when :denied
83
+ return false
84
+ when :allowed
85
+ return true
86
+ else
87
+ end
88
+ end
89
+
90
+ true
91
+ end
92
+
93
+ def explain!
94
+ @explain = true
95
+ end
96
+
97
+ def stop_explaining!
98
+ @explain = false
99
+ end
100
+
101
+ def checkin!(action, object_or_resource)
102
+ raise Checkin::AccessDenied.new(self, action, object_or_resource) unless self.can?(action, object_or_resource)
103
+ end
104
+
105
+ def delete_denied_params(action, object_or_resource, resource_params)
106
+ to_be_deleted = []
107
+ resource_params.keys.each {|key|
108
+ to_be_deleted.push(key) unless self.allowed_to_set?(key, :on => object_or_resource)
109
+ }
110
+ to_be_deleted.each do |key_to_delete|
111
+ resource_params.delete(key_to_delete)
112
+ end
113
+ resource_params
114
+ end
115
+
116
+ def method_missing(mid, *args)
117
+ missing_method = mid.to_s
118
+ prefixed_with_can = (missing_method =~ /^can_/) && (missing_method =~ /\?$/)
119
+
120
+ if prefixed_with_can
121
+ action = missing_method.gsub(/^can_/, "").gsub(/\?$/, "")
122
+ self.can?(action, *args)
123
+
124
+ elsif self.respond_to?(:"is_role_method?") && self.is_role_method?(missing_method)
125
+ role = self.find_role_by_method(missing_method)
126
+ self.check_role(role, *args)
127
+
128
+ elsif @subject_model && @subject_model.respond_to?(missing_method)
129
+ @subject_model.send(missing_method, *args)
130
+
131
+ else
132
+ raise NoMethodError.new("undefined method `#{missing_method}' for #{self.class.name}")
133
+ end
134
+ end
135
+
136
+ end
137
+
data/lib/checkin.rb ADDED
@@ -0,0 +1,32 @@
1
+ # Author:: Maurizio Casimirri (mailto:maurizio.cas@gmail.com)
2
+ # Copyright:: Copyright (c) 2012 Maurizio Casimirri
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+
23
+ module Checkin
24
+ end
25
+
26
+ require 'checkin/filters'
27
+ require 'checkin/subject'
28
+ require 'checkin/access_denied'
29
+
30
+ class ActionController::Base
31
+ include Checkin::Filters
32
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+ require 'shoulda'
12
+
13
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
14
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
+ require 'checkin'
16
+
17
+ class Test::Unit::TestCase
18
+ end
@@ -0,0 +1,7 @@
1
+ require 'helper'
2
+
3
+ class TestCheckin < Test::Unit::TestCase
4
+ should "probably rename this file and start testing for real" do
5
+ flunk "hey buddy, you should probably rename this file and start testing for real"
6
+ end
7
+ end
metadata ADDED
@@ -0,0 +1,84 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: checkin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - mcasimir
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: jeweler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.8.3
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.8.3
30
+ description: Checkin is an authorization gem for Ruby on Rails
31
+ email: maurizio.cas@gmail.com
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files:
35
+ - LICENSE.txt
36
+ - README.md
37
+ files:
38
+ - .document
39
+ - Gemfile
40
+ - Gemfile.lock
41
+ - LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - VERSION
45
+ - checkin.gemspec
46
+ - lib/checkin.rb
47
+ - lib/checkin/access_denied.rb
48
+ - lib/checkin/dsl/permissions.rb
49
+ - lib/checkin/dsl/roles.rb
50
+ - lib/checkin/filters.rb
51
+ - lib/checkin/role.rb
52
+ - lib/checkin/rule.rb
53
+ - lib/checkin/subject.rb
54
+ - test/helper.rb
55
+ - test/test_checkin.rb
56
+ homepage: http://github.com/mcasimir/checkin
57
+ licenses:
58
+ - MIT
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ segments:
70
+ - 0
71
+ hash: 1307085424481181596
72
+ required_rubygems_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ requirements: []
79
+ rubyforge_project:
80
+ rubygems_version: 1.8.24
81
+ signing_key:
82
+ specification_version: 3
83
+ summary: Checkin is an authorization gem for Ruby on Rails
84
+ test_files: []