cannibal 0.5.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/README.mdown ADDED
@@ -0,0 +1,114 @@
1
+ Cannibal
2
+ ========
3
+
4
+ A simple permissions system for declaring and querying permissions between Ruby objects.
5
+
6
+ Background
7
+ ----------
8
+
9
+ Cannibal is based around defining interactions between Actors and Subjects.
10
+
11
+ An Actor participates in your system as an agent that "does" things. Cannibal lets you declare what
12
+ each Actor can do, and Actors are queried to determine whether or not they are permitted to perform
13
+ a particular action.
14
+
15
+ You can turn any class into an Actor by including Cannibal::Actor within it.
16
+
17
+ A Subject participates in your system as something that is acted upon. You declare for each Subject
18
+ the conditions under which each Actor may or may not interact with it.
19
+
20
+ You can turn any class into a Subject by including Cannibal::Actor within it.
21
+
22
+ If for example you are using Cannibal in a Rails application, an example of an Actor might be a
23
+ User model. An example of a Subject might be a Task model. You may want all Users in the system to
24
+ be able to view all of the available Tasks, but you may want to restrict tasks to only be editable
25
+ by their creators.
26
+
27
+ Permissions can be set at various levels, starting at the Class level and getting finer grained
28
+ right down to the field or method level of your models.
29
+
30
+ In addition, you can specify static permissions (no User may modify a Task) or dynamic permissions
31
+ that are evaluated at query time (ie. a specific user may or may not be able to modify a specific
32
+ task, based on whatever rules you put forth).
33
+
34
+ Sample Scenarios
35
+ ----------------
36
+
37
+ Class-level permissions:
38
+
39
+ class User
40
+ include Cannibal::Actor
41
+ end
42
+
43
+ class Thing
44
+ include Cannibal::Subject
45
+ allow User, :edit
46
+ end
47
+
48
+ @user = User.new
49
+ @thing = Thing.new
50
+
51
+ puts "Yay!" if @user.can? :edit, @thing
52
+
53
+ Actor object-level permissions:
54
+
55
+ class User
56
+ include Cannibal::Actor
57
+ attr_accessor :role
58
+ end
59
+
60
+ class Thing
61
+ include Cannibal::Subject
62
+ permission({
63
+ :actor => User,
64
+ :verb => :edit,
65
+ :actor_proc => Proc.new { |user|
66
+ if user.role == 'administrator'
67
+ true
68
+ else
69
+ false
70
+ end
71
+ }
72
+ })
73
+ end
74
+
75
+ @user = User.new; @user.role = 'user'
76
+ @admin = User.new; @user.role = 'administrator'
77
+ @thing = Thing.new
78
+
79
+ puts "Back off!" unless @user.can? :edit, @thing
80
+ puts "Yay!" if @admin.can? :edit, @thing
81
+
82
+ Actor and subject object-to-object level permissions:
83
+
84
+ class User
85
+ include Cannibal::Actor
86
+ attr_accessor :role
87
+ end
88
+
89
+ class Thing
90
+ include Cannibal::Subject
91
+ attr_accessor :owner
92
+ permission({
93
+ :actor => User,
94
+ :verb => :edit,
95
+ :proc => Proc.new { |user, thing|
96
+ if user.role == 'administrator' or user == thing.owner
97
+ true
98
+ else
99
+ false
100
+ end
101
+ }
102
+ })
103
+ end
104
+
105
+ @user_a = User.new; @user.role = 'user'
106
+ @user_b = User.new; @user.role = 'user'
107
+ @admin = User.new; @user.role = 'administrator'
108
+
109
+ @thing = Thing.new; @thing.owner = @user_a
110
+
111
+ puts "Back off!" unless @user_b.can? :edit, @thing
112
+ puts "Yay!" if @user_a.can? :edit, @thing
113
+ puts "Yay!" if @admin.can? :edit, @thing
114
+
@@ -0,0 +1,24 @@
1
+ module Cannibal
2
+ module Actor
3
+
4
+ module ClassMethods
5
+ def can?(verb, subject, attribute=nil)
6
+ permissions.allowed?(self, subject, verb, attribute)
7
+ end
8
+
9
+ def permissions
10
+ @@registry = Cannibal::PermissionRegistry.instance
11
+ end
12
+ end
13
+
14
+ def self.included(klass)
15
+ klass.extend ClassMethods
16
+ end
17
+
18
+ def can?(verb, subject, attribute=nil)
19
+ self.class.permissions.allowed?(self, subject, verb, attribute)
20
+ end
21
+
22
+ end
23
+ end
24
+
@@ -0,0 +1,135 @@
1
+ require 'singleton'
2
+
3
+ module Cannibal
4
+ class PermissionRegistry
5
+ include Singleton
6
+
7
+ def set(options)
8
+ actor = options[:actor]
9
+ verb = options[:verb]
10
+ subject = options[:subject]
11
+
12
+ if actor.is_a? Class
13
+ actor_class = actor
14
+ else
15
+ actor_class = actor.class
16
+ end
17
+
18
+ if subject.is_a? Class
19
+ subject_class = subject
20
+ else
21
+ subject_class = subject.class
22
+ end
23
+
24
+ actor_proc = options[:actor_proc]
25
+ gproc = options[:proc]
26
+
27
+ perm = options[:perm]
28
+ attributes = options[:attribute]
29
+ if attributes.nil?
30
+ # Set class-wide perms if no attributes specified
31
+ verb_hash(actor_class, subject_class, verb)[:perm] = perm unless perm.nil?
32
+ verb_hash(actor_class, subject_class, verb)[:actor_proc] = actor_proc unless actor_proc.nil?
33
+ verb_hash(actor_class, subject_class, verb)[:proc] = gproc unless gproc.nil?
34
+ else
35
+ attributes = [ attributes ] unless attributes.is_a? Array
36
+ attributes.each do |attribute|
37
+ attribute_hash(actor_class, subject_class, verb)[attribute] = {}
38
+ attribute_hash(actor_class, subject_class, verb)[attribute][:perm] = perm unless perm.nil?
39
+ attribute_hash(actor_class, subject_class, verb)[attribute][:actor_proc] = actor_proc unless actor_proc.nil?
40
+ attribute_hash(actor_class, subject_class, verb)[attribute][:proc] = gproc unless gproc.nil?
41
+ end
42
+ end
43
+ end
44
+
45
+ def allowed?(actor, subject, verb, attribute=nil)
46
+ ok = false
47
+
48
+ if actor.is_a? Class
49
+ actor_class = actor
50
+ else
51
+ actor_class = actor.class
52
+ end
53
+ if subject.is_a? Class
54
+ subject_class = subject
55
+ else
56
+ subject_class = subject.class
57
+ end
58
+
59
+ ph = verb_hash(actor_class, subject_class, verb)
60
+ #puts "\n########### PERM HASH FOR #{actor_class} #{subject_class} #{verb} #{attribute}"
61
+ #puts "#{ph.inspect}\n\n"
62
+
63
+ # Check class perms first
64
+ if ph.has_key? :perm
65
+ ok = ph[:perm]
66
+ end
67
+
68
+ unless actor.is_a? Class
69
+ # Allow object perms to override
70
+ if ph.has_key? :actor_proc
71
+ ok = ph[:actor_proc].call actor
72
+ end
73
+ end
74
+
75
+ unless subject.is_a? Class
76
+ # Allow object-to-object perms to override
77
+ if ph.has_key? :proc
78
+ ok = ph[:proc].call actor, subject
79
+ end
80
+ end
81
+
82
+ unless attribute.nil?
83
+ # puts "Evaluating attribute #{attribute}"
84
+ ah = attribute_hash(actor_class, subject_class, verb)
85
+ # puts ah.inspect
86
+ if ah.has_key? attribute
87
+ # puts "Found key #{attribute}"
88
+
89
+ if ah[attribute].has_key? :perm
90
+ # puts "Setting from perm"
91
+ ok = ah[attribute][:perm]
92
+ end
93
+
94
+ unless actor.is_a? Class or ah[attribute][:actor_proc].nil?
95
+ # puts "Setting from actor_proc"
96
+ ok = ah[attribute][:actor_proc].call actor
97
+ end
98
+
99
+ unless subject.is_a? Class or ah[attribute][:proc].nil?
100
+ # puts "Setting from proc"
101
+ ok = ah[attribute][:proc].call actor, subject
102
+ end
103
+ end
104
+ # puts "Found #{ok}"
105
+ end
106
+
107
+ ok
108
+ end
109
+
110
+ def permstore
111
+ @perms ||= {}
112
+ end
113
+
114
+ def reset
115
+ @perms = {}
116
+ end
117
+
118
+ def verb_hash(actor, subject, verb)
119
+ actor_hash = hash_or_init permstore, actor
120
+ subject_hash = hash_or_init actor_hash, subject
121
+ hash_or_init subject_hash, verb
122
+ end
123
+
124
+ def attribute_hash(actor, subject, verb)
125
+ hash_or_init verb_hash(actor, subject, verb), :attributes
126
+ end
127
+
128
+ def hash_or_init(hash, key)
129
+ unless hash.include? key
130
+ hash[key] = {}
131
+ end
132
+ hash[key]
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,44 @@
1
+ module Cannibal
2
+
3
+ module Subject
4
+
5
+ module ClassMethods
6
+
7
+ def allow(actor, verb, attribute=nil)
8
+ permissions.set(
9
+ :actor => actor,
10
+ :verb => verb,
11
+ :subject => self,
12
+ :attribute => attribute,
13
+ :perm => true
14
+ )
15
+ end
16
+
17
+ def deny(actor, verb, attribute=nil)
18
+ permissions.set(
19
+ :actor => actor,
20
+ :verb => verb,
21
+ :subject => self,
22
+ :attribute => attribute,
23
+ :perm => false
24
+ )
25
+ end
26
+
27
+ def permission(options)
28
+ options[:subject] = self
29
+ permissions.set(options)
30
+ end
31
+
32
+ def permissions
33
+ @registry ||= Cannibal::PermissionRegistry.instance
34
+ end
35
+
36
+ end
37
+
38
+ def self.included(klass)
39
+ klass.extend ClassMethods
40
+ end
41
+
42
+ end
43
+ end
44
+
data/lib/cannibal.rb ADDED
@@ -0,0 +1,3 @@
1
+ require 'cannibal/permission_registry'
2
+ require 'cannibal/actor'
3
+ require 'cannibal/subject'
metadata ADDED
@@ -0,0 +1,71 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cannibal
3
+ version: !ruby/object:Gem::Version
4
+ hash: 11
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 5
9
+ - 0
10
+ version: 0.5.0
11
+ platform: ruby
12
+ authors:
13
+ - Three Wise Men
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-08-17 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Use this library in a Ruby application to provide permission declaration and querying capabilities between Ruby objects.
23
+ email: info @nospam@ threewisemen.ca
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - README.mdown
30
+ files:
31
+ - lib/cannibal.rb
32
+ - lib/cannibal/actor.rb
33
+ - lib/cannibal/permission_registry.rb
34
+ - lib/cannibal/subject.rb
35
+ - README.mdown
36
+ has_rdoc: true
37
+ homepage:
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options: []
42
+
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ hash: 3
51
+ segments:
52
+ - 0
53
+ version: "0"
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ">="
58
+ - !ruby/object:Gem::Version
59
+ hash: 3
60
+ segments:
61
+ - 0
62
+ version: "0"
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.7
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: Permission framework for Ruby objects
70
+ test_files: []
71
+