permissable 0.1.0 → 0.2.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.rdoc +37 -11
- data/VERSION +1 -1
- data/lib/permissable/member.rb +74 -7
- data/lib/permissable/resource.rb +1 -4
- data/lib/permissable.rb +24 -9
- data/permissable.gemspec +2 -3
- metadata +4 -5
- data/permissable-0.0.3.gem +0 -0
data/README.rdoc
CHANGED
@@ -4,10 +4,7 @@ Permissable creates the ability to add a permissions system to "resources" based
|
|
4
4
|
It allows you to define a member using the +permissable+ method:
|
5
5
|
|
6
6
|
class User < ActiveRecord::Base
|
7
|
-
|
8
|
-
configure.permission_for :read, :posts, :categories
|
9
|
-
configure.permission_for [:read, :write, :moderate], :comments
|
10
|
-
end
|
7
|
+
has_permissions_for [:section, :category], :to => [:read, :write, :moderate]
|
11
8
|
end
|
12
9
|
|
13
10
|
Permissable uses a single "permissions" table (and subsuquently a Permission model), with a polymorphic relationship
|
@@ -24,18 +21,47 @@ This documention is incomplete... I'll add more later.
|
|
24
21
|
== Usage
|
25
22
|
|
26
23
|
class User < ActiveRecord::Base
|
27
|
-
|
28
|
-
configure.permission_for :read, :posts, :categories
|
29
|
-
configure.permission_for [:read, :write, :moderate], :comments
|
30
|
-
end
|
24
|
+
has_permissions_for [:section, :category], :to => [:read, :write, :moderate]
|
31
25
|
end
|
32
26
|
|
33
|
-
The permissable method accepts
|
34
|
-
Each call to permission_for accepts two attributes.
|
27
|
+
The permissable method accepts two options:
|
35
28
|
|
36
29
|
* method: The permission you want to assign (read, write, moderate, etc)
|
37
|
-
*
|
30
|
+
* options: A hash of options.
|
31
|
+
|
32
|
+
The only required option is the :to key, which defines a number of permisssions to assign to the speficied resource(s)
|
33
|
+
|
34
|
+
== Setting Permissions via Association
|
35
|
+
|
36
|
+
Permissable also allows you to specify permissions via an association. For instance say your User has_many roles, and you would like the
|
37
|
+
permissions to be based on a particular user's roles. To do this, add the :through option when setting permissions:
|
38
|
+
|
39
|
+
:through => :roles
|
40
|
+
|
41
|
+
When permissable does permission lookups on any of the resources specified in has_permissions_for it will use the assocation to define them.
|
42
|
+
Instead of looking up User.permissions, it will now lookup user.roles.permissions.
|
43
|
+
|
44
|
+
== Always allowing specific members
|
45
|
+
|
46
|
+
If you would like can? to always pass for a particular member, use the :allow_with option with has_permissions_for. The allow_with option accepts a method name within your
|
47
|
+
member model that will be called prior to checking permissions from the database.
|
48
|
+
|
49
|
+
class User < ActiveRecord::Base
|
50
|
+
has_permissions_for [:section, :category], :to => [:read, :write, :moderate], :allow_with => :is_admin?
|
51
|
+
|
52
|
+
def is_admin?
|
53
|
+
// your logic here. If this resolves to true... can? will also be true.
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
== Planned Updates
|
58
|
+
|
59
|
+
In the near future there are plans to add the ability to "eager_load" permissions on a user the first time it is created. When using this configuration
|
60
|
+
option, ALL permissions relating to that user are looked up in a single database query, and then cached to the user instance. By default I think this may be the proper way to handle it anyway.
|
38
61
|
|
62
|
+
We are also planning to implement permission "chains". Basically this will define a series of "levels" to your permission. For instance a user
|
63
|
+
who can "write" a model, would also be able to "read" a model. You would be able to tell Permissable which methods chain from each other and
|
64
|
+
the order they should rely on. Think about how a statemachine transitions from one state to another in a way....
|
39
65
|
|
40
66
|
== Note on Patches/Pull Requests
|
41
67
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/permissable/member.rb
CHANGED
@@ -5,27 +5,61 @@ module Permissable
|
|
5
5
|
def self.included(base)
|
6
6
|
base.send :include, InstanceMethods
|
7
7
|
base.send :attr_protected, :member_identifier
|
8
|
+
base.send :attr_protected, :permissable_lookups
|
8
9
|
end
|
9
10
|
|
10
11
|
# This module includes methods that should exist on ALL members.
|
11
12
|
module InstanceMethods
|
12
13
|
|
13
14
|
# The can? method returns a boolen value specifying whether or not this member can perform the specific method on resource
|
14
|
-
def can?(
|
15
|
-
|
15
|
+
def can?(methods, resource)
|
16
|
+
unless allow_permission_with_method.nil?
|
17
|
+
if self.respond_to? "#{allow_permission_with_method}"
|
18
|
+
return true if (send "#{allow_permission_with_method}")
|
19
|
+
end
|
20
|
+
end
|
21
|
+
methods = [methods].flatten.collect{ |m| m.to_sym }
|
22
|
+
methods = find_methods_from_chain(methods)
|
23
|
+
permissions_for(resource, methods).exists?
|
16
24
|
end
|
17
25
|
|
18
26
|
# Alias to can? to get the inverse.
|
19
27
|
def cannot?(method, resource); !can?(method, resource); end
|
20
28
|
|
29
|
+
# Assign permissions to a member if they don't already exist.
|
30
|
+
# TODO: There has to be a friendlier way to mass assign permissions to roles rather than
|
31
|
+
# looping each one for existence and then saving separately.
|
32
|
+
def can!(methods, resources)
|
33
|
+
[resources].flatten.each do |resource|
|
34
|
+
|
35
|
+
# Kind of unecessary but since some methods allow you to specify a Classname directly, this just
|
36
|
+
# safegaurds against trying to do the same here.
|
37
|
+
next if resource.is_a?(Class)
|
38
|
+
|
39
|
+
# Get the member identifier for this resource
|
40
|
+
identifier = member_identifier(resource)
|
41
|
+
|
42
|
+
[methods].flatten.each do |method|
|
43
|
+
# Permission already exists, continue.
|
44
|
+
next if can?(method, resource)
|
45
|
+
|
46
|
+
# Create a new permission for each member (once if its self, multiple times if its associated)
|
47
|
+
[identifier[:member_id]].flatten.each do |member_id|
|
48
|
+
perm = Permission.new(:member_id => member_id, :member_type => identifier[:member_type], :permission_type => method.to_s.downcase)
|
49
|
+
perm.resource = resource
|
50
|
+
perm.save
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
21
56
|
|
22
57
|
# This sets the member information for our permission lookup based on the current resource scope.
|
23
58
|
# These attributes correspond to the correct member_id and member_type in our permissions table.
|
24
|
-
def member_identifier(
|
59
|
+
def member_identifier(resource)
|
25
60
|
|
26
61
|
@member_identifier ||= {}
|
27
|
-
|
28
|
-
scope = scope.to_s.classify
|
62
|
+
scope = fetch_scope(resource)
|
29
63
|
|
30
64
|
return @member_identifier[scope] unless @member_identifier[scope].nil?
|
31
65
|
return { :member_id => self.id, :member_type => self.class.to_s } unless permissable_associations.has_key?(scope)
|
@@ -39,18 +73,51 @@ module Permissable
|
|
39
73
|
|
40
74
|
# Provide an instance method to our associations
|
41
75
|
def permissable_associations; self.class.permissable_associations; end
|
76
|
+
# Find our permission override if available
|
77
|
+
def allow_permission_with_method; self.class.permissable_options[:allow_permission_with_method]; end
|
78
|
+
# See if there is a permissions chain
|
79
|
+
def permission_chain; self.class.permissable_options[:permission_chain] || {}; end
|
42
80
|
|
43
81
|
private
|
44
82
|
|
83
|
+
def find_methods_from_chain(methods)
|
84
|
+
|
85
|
+
return methods if permission_chain.empty?
|
86
|
+
allowed_methods = []
|
87
|
+
|
88
|
+
methods.each do |method|
|
89
|
+
permission_chain.each_pair do |key, value|
|
90
|
+
value = [value].flatten.collect{ |v| v.to_sym }
|
91
|
+
allowed_methods << key.to_sym if value.include?(method)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
allowed_methods << methods
|
96
|
+
allowed_methods.flatten.uniq
|
97
|
+
|
98
|
+
end
|
99
|
+
|
45
100
|
# Looks up permissions for a particular resource.
|
46
101
|
def permissions_for(resource, methods = nil)
|
47
|
-
scope = resource
|
48
|
-
return self.permissions unless permissable_associations.has_key?(scope)
|
102
|
+
scope = fetch_scope(resource)
|
103
|
+
return self.permissions.with_permission_to(methods) unless permissable_associations.has_key?(scope)
|
49
104
|
relation = Permission.where(member_identifier(scope)).for_resource(resource)
|
50
105
|
relation = relation.with_permission_to(methods) unless methods.nil?
|
51
106
|
relation
|
52
107
|
end
|
53
108
|
|
109
|
+
# Returns the member responsible for this resource. This can either be an instance of self, or an instance or
|
110
|
+
# array of assocated instances.
|
111
|
+
def permissable_member(resource)
|
112
|
+
scope = fetch_scope(resource)
|
113
|
+
(permissable_associations.has_key?(scope)) ? send("#{permissable_associations[scope]}".to_sym) : self
|
114
|
+
end
|
115
|
+
|
116
|
+
def fetch_scope(resource)
|
117
|
+
return resource if resource.is_a?(String)
|
118
|
+
(resource.is_a?(Class)) ? resource.to_s : resource.class.to_s.classify
|
119
|
+
end
|
120
|
+
|
54
121
|
end
|
55
122
|
|
56
123
|
end
|
data/lib/permissable/resource.rb
CHANGED
data/lib/permissable.rb
CHANGED
@@ -56,6 +56,7 @@ module Permissable
|
|
56
56
|
raise Permissable::PermissionNotDefined, "has_permissions_for missing the :to option." unless options.has_key?(:to) and !options[:to].empty?
|
57
57
|
|
58
58
|
write_inheritable_attribute(:permissable_associations, {}) if permissable_associations.nil?
|
59
|
+
write_inheritable_attribute(:permissable_options, {}) if permissable_options.nil?
|
59
60
|
resources = [resources].flatten
|
60
61
|
|
61
62
|
resources.each do |resource|
|
@@ -69,33 +70,47 @@ module Permissable
|
|
69
70
|
assoc = options[:through].to_s.classify.constantize
|
70
71
|
|
71
72
|
# Our association also creates a has_many association on our permissions table.
|
72
|
-
assoc.class_eval do
|
73
|
+
assoc.class_eval do
|
73
74
|
has_many(:permissions, :as => :member, :conditions => { :member_type => "#{self.to_s}" }) unless respond_to? :permissions
|
75
|
+
include Permissable::Member
|
76
|
+
class_inheritable_accessor :permission_types
|
77
|
+
write_inheritable_attribute(:permissable_associations, {})
|
78
|
+
write_inheritable_attribute(:permissable_options, {}) if permissable_options.nil?
|
79
|
+
permissable_options[:allow_permission_with_method] = options[:allow_with] if options.has_key?(:allow_with)
|
80
|
+
permissable_options[:permission_chain] = options[:chain] if options.has_key?(:chain)
|
81
|
+
self.send :permission_types=, options[:to]
|
74
82
|
end
|
75
|
-
|
83
|
+
|
76
84
|
end
|
77
|
-
|
85
|
+
|
78
86
|
# Setup a has_many association of permissions on our resource.
|
79
|
-
resource.constantize.class_eval do
|
80
|
-
include Permissable::Resource
|
87
|
+
resource.constantize.class_eval do
|
81
88
|
has_many(:permissions, :as => :resource, :conditions => { :resource_type => "#{self.to_s}" }) unless respond_to? :permissions
|
82
89
|
end
|
83
90
|
|
91
|
+
resource.constantize.instance_eval{ include Permissable::Resource }
|
92
|
+
|
84
93
|
end
|
85
94
|
|
95
|
+
permissable_options[:allow_permission_with_method] = options[:allow_with] if options.has_key?(:allow_with)
|
96
|
+
permissable_options[:permission_chain] = options[:chain] if options.has_key?(:chain)
|
97
|
+
|
86
98
|
# This class becomes a member to resources.
|
87
99
|
include Permissable::Member
|
100
|
+
class_inheritable_accessor :permission_types
|
101
|
+
self.send :permission_types=, options[:to]
|
88
102
|
|
89
103
|
# Members create a has_many association on permissions as a member.
|
90
104
|
has_many(:permissions, :as => :member, :conditions => { :member_type => "#{self.to_s}" }) unless respond_to? :permissions
|
91
105
|
|
92
106
|
end
|
93
107
|
|
108
|
+
# Access options such as our method override, or our permission chain.
|
109
|
+
def permissable_options; read_inheritable_attribute(:permissable_options); end
|
110
|
+
|
94
111
|
# Each time has_permissions_for is called, different associations may exist.
|
95
|
-
# This provides a way to store and update them all as necessary.
|
96
|
-
def permissable_associations
|
97
|
-
read_inheritable_attribute(:permissable_associations)
|
98
|
-
end
|
112
|
+
# This provides a way to store and update them all as necessary.
|
113
|
+
def permissable_associations; read_inheritable_attribute(:permissable_associations); end
|
99
114
|
|
100
115
|
end
|
101
116
|
end
|
data/permissable.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{permissable}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Brent Kirby"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-09-04}
|
13
13
|
s.description = %q{Permissable lets you set a number of 'permissions' to your database models. By assigning 'members' to 'resources' (models) you can specify various permission states.}
|
14
14
|
s.email = %q{brent@kurbmedia.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -28,7 +28,6 @@ Gem::Specification.new do |s|
|
|
28
28
|
"lib/permissable/member.rb",
|
29
29
|
"lib/permissable/permission.rb",
|
30
30
|
"lib/permissable/resource.rb",
|
31
|
-
"permissable-0.0.3.gem",
|
32
31
|
"permissable.gemspec",
|
33
32
|
"test/helper.rb",
|
34
33
|
"test/test_permissable.rb"
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: permissable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 23
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
|
-
-
|
8
|
+
- 2
|
9
9
|
- 0
|
10
|
-
version: 0.
|
10
|
+
version: 0.2.0
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Brent Kirby
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-
|
18
|
+
date: 2010-09-04 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -53,7 +53,6 @@ files:
|
|
53
53
|
- lib/permissable/member.rb
|
54
54
|
- lib/permissable/permission.rb
|
55
55
|
- lib/permissable/resource.rb
|
56
|
-
- permissable-0.0.3.gem
|
57
56
|
- permissable.gemspec
|
58
57
|
- test/helper.rb
|
59
58
|
- test/test_permissable.rb
|
data/permissable-0.0.3.gem
DELETED
Binary file
|