checkin 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.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: []