thwart 0.0.0
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 +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
|