thwart 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +24 -0
- data/LICENSE +20 -0
- data/README.rdoc +17 -0
- data/Rakefile +30 -0
- data/VERSION +1 -0
- data/autotest/discover.rb +1 -0
- data/examples/a_complete_example.rb +56 -0
- data/examples/example_helper.rb +45 -0
- data/lib/thwart/action_group_builder.rb +62 -0
- data/lib/thwart/actions_store.rb +89 -0
- data/lib/thwart/actor.rb +36 -0
- data/lib/thwart/canable.rb +30 -0
- data/lib/thwart/dsl.rb +43 -0
- data/lib/thwart/enforcer.rb +15 -0
- data/lib/thwart/resource.rb +27 -0
- data/lib/thwart/role.rb +81 -0
- data/lib/thwart/role_builder.rb +143 -0
- data/lib/thwart/role_registry.rb +75 -0
- data/lib/thwart.rb +67 -0
- data/spec/action_group_builder_spec.rb +68 -0
- data/spec/actions_store_spec.rb +74 -0
- data/spec/actor_spec.rb +45 -0
- data/spec/canable_spec.rb +41 -0
- data/spec/dsl_spec.rb +103 -0
- data/spec/enforcer_spec.rb +17 -0
- data/spec/resource_spec.rb +42 -0
- data/spec/role_builder_spec.rb +197 -0
- data/spec/role_registry_spec.rb +137 -0
- data/spec/role_spec.rb +146 -0
- data/spec/spec_helper.rb +49 -0
- data/spec/thwart_spec.rb +60 -0
- metadata +139 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Harry Brundage
|
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.rdoc
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
= thwart
|
2
|
+
|
3
|
+
Simple, powerful, and developer friendly authorization plugin.
|
4
|
+
|
5
|
+
== Note on Patches/Pull Requests
|
6
|
+
|
7
|
+
* Fork the project.
|
8
|
+
* Make your feature addition or bug fix.
|
9
|
+
* Add tests for it. This is important so I don't break it in a
|
10
|
+
future version unintentionally.
|
11
|
+
* Commit, do not mess with rakefile, version, or history.
|
12
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
13
|
+
* Send me a pull request. Bonus points for topic branches.
|
14
|
+
|
15
|
+
== Copyright
|
16
|
+
|
17
|
+
Copyright (c) 2010 Harry Brundage. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "thwart"
|
8
|
+
gem.summary = %Q{A simple, powerful, and developer friendly authorization plugin. Still WIP.}
|
9
|
+
gem.description = %Q{Implements a robust Role Based Access System where Actors are granted permission to preform Actions by playing any number of Roles. All defined programatically in one place using a super easy DSL.}
|
10
|
+
gem.email = "harry@skylightlabs.ca"
|
11
|
+
gem.homepage = "http://github.com/hornairs/thwart"
|
12
|
+
gem.authors = ["Harry Brundage"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 2.0.0.beta19"
|
14
|
+
gem.add_dependency "activesupport", ">= 3.0.rc1"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'rake/rdoctask'
|
23
|
+
Rake::RDocTask.new do |rdoc|
|
24
|
+
version = File.exist?('VERSION') ? File.read('VERSION') : ""
|
25
|
+
|
26
|
+
rdoc.rdoc_dir = 'rdoc'
|
27
|
+
rdoc.title = "thwart #{version}"
|
28
|
+
rdoc.rdoc_files.include('README*')
|
29
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
30
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.0
|
@@ -0,0 +1 @@
|
|
1
|
+
Autotest.add_discovery { "rspec2" }
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/example_helper')
|
2
|
+
|
3
|
+
Thwart.configure do
|
4
|
+
Thwart::Actions.add_crud!
|
5
|
+
default_query_response false
|
6
|
+
|
7
|
+
action_group :manage, [:view, :create, :update, :destroy]
|
8
|
+
|
9
|
+
role :employee do
|
10
|
+
view :all
|
11
|
+
update :this, :that
|
12
|
+
end
|
13
|
+
|
14
|
+
role :manager, :include => :employee do
|
15
|
+
allow do
|
16
|
+
destroy :this
|
17
|
+
end
|
18
|
+
deny do
|
19
|
+
destroy :that
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
role :administrator do
|
24
|
+
manage :all
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
@ed = User.new('Ed', :employee)
|
29
|
+
@mary = User.new('Mary', :manager)
|
30
|
+
@admin = User.new('Admin', :administrator)
|
31
|
+
|
32
|
+
@a_this = This.new
|
33
|
+
@a_that = That.new
|
34
|
+
@a_then = Then.new
|
35
|
+
|
36
|
+
puts @ed.can_view?(@a_this)
|
37
|
+
puts @ed.can_view?(@a_that)
|
38
|
+
puts @ed.can_view?(@a_then)
|
39
|
+
puts @ed.can_update?(@a_this)
|
40
|
+
puts @ed.can_update?(@a_that)
|
41
|
+
puts @ed.can_update?(@a_then)
|
42
|
+
|
43
|
+
# Thwart.query(:employee, :this, :view).should == true
|
44
|
+
# Thwart.query(:employee, @a_this, :view).should == true
|
45
|
+
# Thwart.query(@ed, :this, :view).should == true
|
46
|
+
# Thwart.query(@ed, @a_this, :view).should == true
|
47
|
+
#
|
48
|
+
# Thwart.query(:employee, :this, :update).should == true
|
49
|
+
# Thwart.query(:employee, @a_this, :update).should == true
|
50
|
+
# Thwart.query(@ed, :this, :update).should == true
|
51
|
+
# Thwart.query(@ed, @a_this, :update).should == true
|
52
|
+
#
|
53
|
+
# Thwart.query(:employee, :then, :update).should == true
|
54
|
+
# Thwart.query(:employee, @a_then, :update).should == true
|
55
|
+
# Thwart.query(@ed, :then, :update).should == true
|
56
|
+
# Thwart.query(@ed, @a_then, :update).should == true
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
require 'thwart'
|
5
|
+
|
6
|
+
# require 'rubygems'
|
7
|
+
# require 'ruby-debug'
|
8
|
+
|
9
|
+
class User
|
10
|
+
include Thwart::Actor
|
11
|
+
thwart_access do
|
12
|
+
role_method :role
|
13
|
+
end
|
14
|
+
attr_accessor :name, :role
|
15
|
+
def initialize(n, r)
|
16
|
+
self.name = n
|
17
|
+
self.role = r
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Thing
|
22
|
+
include Thwart::Resource
|
23
|
+
thwart_access do
|
24
|
+
name :thing
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class This < Thing
|
29
|
+
thwart_access do
|
30
|
+
name :this
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class That < Thing
|
35
|
+
thwart_access do
|
36
|
+
name :that
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class Then < Thing
|
41
|
+
thwart_access do
|
42
|
+
name :then
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Thwart
|
2
|
+
class ActionGroupBuilder
|
3
|
+
# Holds all the different action groups in actionables[:name] => [array of resolved actions]
|
4
|
+
attr_accessor :actionables
|
5
|
+
def actionables
|
6
|
+
@actionables ||= {}
|
7
|
+
@actionables
|
8
|
+
end
|
9
|
+
|
10
|
+
# Adds an actionable to the list of actionable things.
|
11
|
+
# @param name [symbol] The name of the action
|
12
|
+
# @param actions [symbol|array|nil] The actions this actionable resolves to. If nil, this is set to the name argument.
|
13
|
+
def add_actionable(name, actions = nil)
|
14
|
+
if actions.nil?
|
15
|
+
actions = Array.wrap(name)
|
16
|
+
else
|
17
|
+
actions = Array.wrap(actions)
|
18
|
+
end
|
19
|
+
self.actionables[name] = actions
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates an action group from an array of actionables.
|
23
|
+
# @param name [symbol] The name of the new action group
|
24
|
+
# @param others [symbol|array] One or an array symbols reffering to action or action groups
|
25
|
+
def create_action_group(name, others)
|
26
|
+
self.add_actionable(name, resolve_action_group(others))
|
27
|
+
end
|
28
|
+
|
29
|
+
# Resolves some actionables recursively down to the raw actions it corresponds to.
|
30
|
+
# @params name [symbol|array] the symbol (array) referring to the existing actionables
|
31
|
+
def resolve_action_group(name)
|
32
|
+
# - if name is an array => resolve it recursively
|
33
|
+
# - if name is in the action groups, pull out its existing resolution
|
34
|
+
# - otherwise, raise an error because we don't know what this is.
|
35
|
+
# Simple action groups (an action itself) must be added to the actions before they
|
36
|
+
# are referenced, which is accomplished using the :save callback on Actions
|
37
|
+
|
38
|
+
return name.map{|n| resolve_action_group(n)}.flatten.uniq if name.respond_to?(:map)
|
39
|
+
return self.actionables[name].flatten.uniq if self.actionables.include?(name)
|
40
|
+
puts self.actionables
|
41
|
+
raise Thwart::ActionOrGroupNotFoundError, "Action or group #{name} could not be found!"
|
42
|
+
end
|
43
|
+
|
44
|
+
# Adds the :create, :read, :update, and :destroy actionables
|
45
|
+
def add_crud_group!
|
46
|
+
@actions_store.add_crud! if @actions_store.respond_to?(:add_crud!)
|
47
|
+
if @crud.nil? || @crud == false
|
48
|
+
self.create_action_group(:crud, Thwart::CrudActions.keys)
|
49
|
+
@crud = true
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def initialize(actions_store = Thwart::Actions)
|
54
|
+
builder = self
|
55
|
+
@actions_store = actions_store
|
56
|
+
@actions_store.class.set_callback :add, :after do |object|
|
57
|
+
builder.add_actionable(actions_store.last_action)
|
58
|
+
end
|
59
|
+
builder
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
module Thwart
|
2
|
+
class ActionsStore
|
3
|
+
include ActiveSupport::Callbacks
|
4
|
+
attr_accessor :last_action
|
5
|
+
attr_reader :actions
|
6
|
+
define_callbacks :add
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@actions = {}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Returns true if Thwart is providing methods for the [action-able]_by? style action
|
13
|
+
# identifier, false otherwise.
|
14
|
+
#
|
15
|
+
# @param [Symbol] able_method The name of the [action-able]_by? method to check for.
|
16
|
+
def has_able?(able)
|
17
|
+
self.actions.has_value?(able)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns true if Thwart is providing methods for the can_[action]? style action
|
21
|
+
# identifer, false otherwise.
|
22
|
+
#
|
23
|
+
# @param [Symbol] able_method The name of the can_[action]? method to check for.
|
24
|
+
def has_can?(can)
|
25
|
+
self.actions.has_key?(can)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Finds the corresponding can from an able.
|
29
|
+
#
|
30
|
+
# @param [Symbol] able_method The name of the [action-able]_by? method.
|
31
|
+
def can_from_able(able)
|
32
|
+
self.actions.key(able)
|
33
|
+
end
|
34
|
+
|
35
|
+
# Adds an action to actions and the correct methods to can and able modules.
|
36
|
+
#
|
37
|
+
# @param [Symbol] can_method The name of the can_[action]? method.
|
38
|
+
# @param [Symbol] resource_method The name of the [action-able]_by? method.
|
39
|
+
def create_action(can, able = nil)
|
40
|
+
if able.nil?
|
41
|
+
if can.respond_to?(:each)
|
42
|
+
can.each {|c,a| self.create_action(c,a)}
|
43
|
+
else
|
44
|
+
raise ArgumentError, "able can't be nil"
|
45
|
+
end
|
46
|
+
else
|
47
|
+
run_callbacks :add do
|
48
|
+
@actions[can] = able
|
49
|
+
@last_action = can
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Finds the able name of the action from a [action-able]_by? style method.
|
55
|
+
#
|
56
|
+
# @param [Symbol] resource_method The name of the [action-able]_by? method.
|
57
|
+
# Adds an action to actions and the correct methods to can and able modules.
|
58
|
+
def find_able(name)
|
59
|
+
md = name.to_s.match(/(.+)_by\?/)
|
60
|
+
if md != nil && self.has_able?(md[1].intern)
|
61
|
+
md[1].intern
|
62
|
+
else
|
63
|
+
false
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Finds the can name of the action from a can_[action]? style method.
|
68
|
+
#
|
69
|
+
# @param [Symbol] can_method The name of the can_[action]? method.
|
70
|
+
def find_can(name)
|
71
|
+
md = name.to_s.match(/can_(.+)\?/)
|
72
|
+
if md != nil && self.has_can?(md[1].intern)
|
73
|
+
md[1].intern
|
74
|
+
else
|
75
|
+
false
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Add the CRUD methods to the Thwart actions (create, read, update, destroy)
|
80
|
+
def add_crud!
|
81
|
+
if @crud.nil? || @crud == false
|
82
|
+
Thwart::CrudActions.each do |(k,v)|
|
83
|
+
self.create_action(k, v)
|
84
|
+
end
|
85
|
+
@crud = true
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
data/lib/thwart/actor.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Thwart
|
2
|
+
module Actor
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
attr_accessor :default_role, :role_from
|
9
|
+
# Thwart enabling hook on actors
|
10
|
+
def thwart_access(&block)
|
11
|
+
self.instance_eval do
|
12
|
+
include Thwart::Cans
|
13
|
+
include Thwart::Actor::InstanceMethods
|
14
|
+
end
|
15
|
+
# Set up DSL using dsl helper
|
16
|
+
if block_given?
|
17
|
+
dsl = Thwart::Dsl.new(:role_method => :role_from=, :role_proc => :role_from=, :role => :default_role=)
|
18
|
+
dsl.evaluate(self, &block)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
module InstanceMethods
|
24
|
+
# The role of this particular actor instance
|
25
|
+
# If the class level @role_from is a symbol, it is the name of the instance method to get the role
|
26
|
+
# If the class level @role_from is a proc, call it
|
27
|
+
# If the above are unsuccessful, use the default if it exists
|
28
|
+
def thwart_role
|
29
|
+
r = self.send(self.class.role_from) if self.class.role_from.is_a?(Symbol) && self.respond_to?(self.class.role_from)
|
30
|
+
r ||= self.class.role_from.call(self) if self.class.role_from.respond_to?(:call)
|
31
|
+
r ||= self.class.default_role unless self.class.default_role.nil?
|
32
|
+
r
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Thwart
|
2
|
+
# Module in which the can_[action]? methods hang out
|
3
|
+
module Cans
|
4
|
+
|
5
|
+
def respond_to?(name)
|
6
|
+
return true if Thwart::Actions.find_can(name) != false
|
7
|
+
super
|
8
|
+
end
|
9
|
+
|
10
|
+
def method_missing(name, *args)
|
11
|
+
can = Thwart::Actions.find_can(name)
|
12
|
+
return Thwart.query(self, args.first, can) if args.length == 1 && !!can
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Module in which the [action]able_by? methods hang out
|
18
|
+
module Ables
|
19
|
+
def respond_to?(name)
|
20
|
+
return true if Thwart::Actions.find_able(name) != false
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(name, *args)
|
25
|
+
able = Thwart::Actions.find_able(name)
|
26
|
+
return Thwart.query(args.first, self, Thwart::Actions.can_from_able(able)) if args.length == 1 && !!able
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/thwart/dsl.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Thwart
|
2
|
+
class DslError < NoMethodError; end
|
3
|
+
class Dsl
|
4
|
+
attr_accessor :extra_methods, # Hash of the extra method mappings of this DSL => target
|
5
|
+
:method_map, # Holds the whole method map hash
|
6
|
+
:target, # What object the DSL maps methods on to
|
7
|
+
:all # Wheather or not to allow all methods (including dynamic ones like method missing) to be mapped
|
8
|
+
|
9
|
+
def initialize(map = {})
|
10
|
+
self.extra_methods = map
|
11
|
+
end
|
12
|
+
|
13
|
+
def evaluate(a_target, &block)
|
14
|
+
self.target = a_target
|
15
|
+
self.method_map = target.public_methods.inject({}) do |acc, m|
|
16
|
+
key = m.to_s.gsub("=", "").intern
|
17
|
+
acc[key] = m if acc[key].nil? || m != key
|
18
|
+
acc
|
19
|
+
end.merge(self.extra_methods)
|
20
|
+
|
21
|
+
self.instance_eval(&block)
|
22
|
+
self.target
|
23
|
+
end
|
24
|
+
|
25
|
+
def respond_to?(name)
|
26
|
+
if @all
|
27
|
+
return target.respond_to?(name)
|
28
|
+
else
|
29
|
+
return true if self.method_map.has_key?(name) && !!self.method_map[name]
|
30
|
+
super
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(name, *args, &block)
|
35
|
+
if self.respond_to?(name)
|
36
|
+
return self.target.send(self.method_map[name], *args, &block) if self.method_map.has_key?(name)
|
37
|
+
return self.target.send(name, *args, &block) if @all
|
38
|
+
end
|
39
|
+
super
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Thwart
|
2
|
+
module Enforcer
|
3
|
+
def thwart_access(resource)
|
4
|
+
raise ArgumentError, "Thwart needs a current_user method to enforce permissions." unless self.respond_to?(:current_user)
|
5
|
+
raise ArgumentError, "Thwart needs the params hash to have an [:action] to enforce." if params.nil? || params[:action].nil?
|
6
|
+
raise ArgumentError, "Unknown action #{params[:action]} to enforce" unless Thwart::Actions.has_can?(params[:action])
|
7
|
+
|
8
|
+
unless Thwart.query(current_user, resource, params[:action])
|
9
|
+
raise Thwart::NoPermissionError, "User #{current_user} doesn't have permission to #{params[:action]} #{resource}."
|
10
|
+
else
|
11
|
+
true
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
module Thwart::Resource
|
3
|
+
include Thwart::Ables
|
4
|
+
# Included hooks
|
5
|
+
def self.included(base)
|
6
|
+
base.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
attr_accessor :thwart_name
|
11
|
+
|
12
|
+
def thwart_access(&block)
|
13
|
+
self.thwart_name = ActiveSupport::Inflector.singularize(self.table_name) if self.respond_to?(:table_name)
|
14
|
+
|
15
|
+
# Set up DSL using dsl helper
|
16
|
+
if block_given?
|
17
|
+
dsl = Thwart::Dsl.new(:name => :thwart_name=)
|
18
|
+
dsl.evaluate(self, &block)
|
19
|
+
end
|
20
|
+
self.ensure_attributes_set!
|
21
|
+
end
|
22
|
+
|
23
|
+
def ensure_attributes_set!
|
24
|
+
raise Thwart::MissingAttributeError if self.thwart_name == nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/lib/thwart/role.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module Thwart
|
2
|
+
|
3
|
+
module Role
|
4
|
+
attr_accessor :name, :default_response, :responses
|
5
|
+
|
6
|
+
def responses
|
7
|
+
@responses ||= {}
|
8
|
+
@responses
|
9
|
+
end
|
10
|
+
|
11
|
+
def parents
|
12
|
+
@parents ||= []
|
13
|
+
@parents
|
14
|
+
end
|
15
|
+
|
16
|
+
def parents=(p)
|
17
|
+
@parents = p.uniq
|
18
|
+
@parents
|
19
|
+
end
|
20
|
+
|
21
|
+
def query(actor, resource, action)
|
22
|
+
@query_result_found = false
|
23
|
+
resp = nil
|
24
|
+
|
25
|
+
if self.responses.has_key?(action)
|
26
|
+
# Find the resource scope response if it exists {:view => {:foo => bool}}
|
27
|
+
resp = self.resource_response(self.responses[action], resource) if !found?
|
28
|
+
# Find the action scope response if it exists {:view => bool}
|
29
|
+
resp = self.action_response(action) if !found?
|
30
|
+
end
|
31
|
+
|
32
|
+
# Return the default if it exists
|
33
|
+
resp = found!(self.default_response) if !found? && !self.default_response.nil?
|
34
|
+
# Call it if it is a proc
|
35
|
+
resp = resp.call(actor, resource, action) if resp.respond_to?(:call)
|
36
|
+
|
37
|
+
return resp
|
38
|
+
end
|
39
|
+
|
40
|
+
def resource_response(resources, resource)
|
41
|
+
# Return the resource scoped response if it exists
|
42
|
+
if resources.respond_to?(:[]) && resources.respond_to?(:include?)
|
43
|
+
if resources.include?(resource)
|
44
|
+
return found!(resources[resource])
|
45
|
+
elsif resources.include?(:_other)
|
46
|
+
return found!(resources[:_other])
|
47
|
+
end
|
48
|
+
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
def action_response(action)
|
53
|
+
# Return the action level boolean, proc, or nil if it exists is the responses array
|
54
|
+
response = self.responses[action]
|
55
|
+
if self.responses.has_key?(action) && (response.is_a?(TrueClass) || response.is_a?(FalseClass) || response.nil? || response.respond_to?(:call))
|
56
|
+
return found!(response)
|
57
|
+
end
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def found!(response)
|
64
|
+
@query_result_found = true
|
65
|
+
response
|
66
|
+
end
|
67
|
+
|
68
|
+
def found?
|
69
|
+
@query_result_found ||= false
|
70
|
+
@query_result_found == true
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class DefaultRole
|
75
|
+
include Thwart::Role
|
76
|
+
def query(*args)
|
77
|
+
return Thwart.default_query_response
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|