credentials 2.2.3 → 2.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +4 -0
- data/VERSION +1 -1
- data/credentials.gemspec +2 -2
- data/lib/credentials/extensions/action_controller.rb +10 -3
- data/lib/credentials/rule.rb +33 -2
- data/lib/credentials/rulebook.rb +17 -0
- data/lib/credentials.rb +4 -0
- data/spec/controllers/test_controller_spec.rb +29 -1
- data/spec/credentials_spec.rb +9 -1
- data/spec/domain.rb +3 -0
- metadata +2 -2
data/HISTORY
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.
|
1
|
+
2.3.0
|
data/credentials.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{credentials}
|
8
|
-
s.version = "2.
|
8
|
+
s.version = "2.3.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Matt Powell"]
|
12
|
-
s.date = %q{2009-
|
12
|
+
s.date = %q{2009-12-16}
|
13
13
|
s.description = %q{A generic actor/resource permission framework based on rules, not objects.}
|
14
14
|
s.email = %q{fauxparse@gmail.com.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -47,8 +47,8 @@ module Credentials
|
|
47
47
|
# otherwise Credentials would also try to evaluate the
|
48
48
|
# +edit+ method as an argument.
|
49
49
|
def requires_permission_to(*args)
|
50
|
-
options = (args.last.is_a?(Hash) ? args.pop : {})
|
51
|
-
|
50
|
+
options = (args.last.is_a?(Hash) ? args.pop : {})
|
51
|
+
[ :only, :except ].each do |key|
|
52
52
|
options[key] = Array(options[key]).map(&:to_sym) if options[key]
|
53
53
|
end
|
54
54
|
self.required_credentials = self.required_credentials + [ [ options, args ] ]
|
@@ -80,12 +80,19 @@ module Credentials
|
|
80
80
|
self.class.required_credentials.each do |options, args|
|
81
81
|
next if options[:only] && !options[:only].include?(current_action)
|
82
82
|
next if options[:except] && options[:except].include?(current_action)
|
83
|
-
|
83
|
+
|
84
84
|
raise Credentials::Errors::NotLoggedInError unless current_user
|
85
85
|
evaluated = args.map do |arg|
|
86
86
|
(arg.is_a?(Symbol) && respond_to?(arg) && !public_methods.include?(arg.to_s)) ? send(arg) : arg
|
87
87
|
end
|
88
88
|
|
89
|
+
opts = returning({}) do |hash|
|
90
|
+
(Credentials::Prepositions & options.keys).each do |prep|
|
91
|
+
hash[prep] = send(options[prep])
|
92
|
+
end
|
93
|
+
end
|
94
|
+
evaluated << opts
|
95
|
+
|
89
96
|
unless current_user.can?(*evaluated)
|
90
97
|
raise Credentials::Errors::AccessDeniedError
|
91
98
|
end
|
data/lib/credentials/rule.rb
CHANGED
@@ -33,7 +33,7 @@ module Credentials
|
|
33
33
|
# match any element of that array:
|
34
34
|
# class User
|
35
35
|
# credentials do |user|
|
36
|
-
# user.can :fight, [ :shatner, :gandhi ]
|
36
|
+
# user.can [ :punch, :fight ], [ :shatner, :gandhi ]
|
37
37
|
# end
|
38
38
|
# end
|
39
39
|
#
|
@@ -47,8 +47,10 @@ module Credentials
|
|
47
47
|
# end
|
48
48
|
# end
|
49
49
|
def match?(*args)
|
50
|
-
|
50
|
+
values = args.last.is_a?(Hash) ? args.pop : {}
|
51
51
|
|
52
|
+
return false unless arity == args.length
|
53
|
+
|
52
54
|
parameters.zip(args).each do |expected, actual|
|
53
55
|
case expected
|
54
56
|
when :self then return false unless actual == args.first
|
@@ -56,7 +58,9 @@ module Credentials
|
|
56
58
|
else return false unless expected === actual
|
57
59
|
end
|
58
60
|
end
|
61
|
+
|
59
62
|
result = true
|
63
|
+
result = result && (options.keys & Credentials::Prepositions).inject(true) { |memo, key| memo && evaluate_preposition(args.first, options[key], values[key]) }
|
60
64
|
result = result && evaluate_condition(options[:if], :|, *args) unless options[:if].nil?
|
61
65
|
result = result && !evaluate_condition(options[:unless], :&, *args) unless options[:unless].nil?
|
62
66
|
result
|
@@ -84,5 +88,32 @@ module Credentials
|
|
84
88
|
end
|
85
89
|
end
|
86
90
|
end
|
91
|
+
|
92
|
+
def evaluate_preposition(object, expected, actual)
|
93
|
+
return true if expected.nil?
|
94
|
+
return false if actual.nil?
|
95
|
+
return true if expected === actual
|
96
|
+
|
97
|
+
single = expected.to_s
|
98
|
+
plural = single.respond_to?(:pluralize) ? single.pluralize : single + "s"
|
99
|
+
|
100
|
+
lclass, rclass = [ object.class.name, actual.class.name ].map do |s|
|
101
|
+
s.gsub(/^.*::/, '').
|
102
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
103
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
104
|
+
tr("-", "_").
|
105
|
+
downcase
|
106
|
+
end
|
107
|
+
|
108
|
+
if object.respond_to?(:id) && actual.respond_to?(:"#{lclass}_id")
|
109
|
+
return true if actual.send(:"#{lclass}_id") == object.id
|
110
|
+
end
|
111
|
+
|
112
|
+
if actual.respond_to?(:id) && object.respond_to?(:"#{rclass}_id")
|
113
|
+
return true if object.send(:"#{rclass}_id") == actual.id
|
114
|
+
end
|
115
|
+
|
116
|
+
(object.respond_to?(single) && (object.send(single) == actual)) || (object.respond_to?(plural) && (object.send(plural).include?(actual)))
|
117
|
+
end
|
87
118
|
end
|
88
119
|
end
|
data/lib/credentials/rulebook.rb
CHANGED
@@ -123,6 +123,23 @@ module Credentials
|
|
123
123
|
# user.can :edit, :self
|
124
124
|
# end
|
125
125
|
# end
|
126
|
+
#
|
127
|
+
# == Prepositions (+:for+, +:on+, etc)
|
128
|
+
# You can do the following:
|
129
|
+
# class User
|
130
|
+
# credentials do |user|
|
131
|
+
# user.can :delete, Comment, :on => :post
|
132
|
+
# end
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# user.can? :delete, post.comments.first, :on => post
|
136
|
+
# ...means that Credentials will check if:
|
137
|
+
# * +post+ has a +user_id+ method matching +user.id+
|
138
|
+
# * +user+ has a +post_id+ method matching +post.id+
|
139
|
+
# * +user+ has a +post+ method matching +post+
|
140
|
+
# * +user+ has a +posts+ method that returns an array including +post+
|
141
|
+
#
|
142
|
+
# See Credentials::Prepositions for the list of available prepositions.
|
126
143
|
def can(*args)
|
127
144
|
self.rules << AllowRule.new(klass, *args)
|
128
145
|
end
|
data/lib/credentials.rb
CHANGED
@@ -2,6 +2,10 @@ require "credentials/rulebook"
|
|
2
2
|
require "credentials/extensions/object"
|
3
3
|
require "credentials/extensions/magic_methods"
|
4
4
|
|
5
|
+
module Credentials
|
6
|
+
Prepositions = [ :on, :for, :with, :at, :in, :from ].freeze
|
7
|
+
end
|
8
|
+
|
5
9
|
Object.send :include, Credentials::Extensions::Object
|
6
10
|
|
7
11
|
if defined?(ActionController)
|
@@ -5,10 +5,12 @@ if defined?(ActionController)
|
|
5
5
|
self.current_user_method = :logged_in_user
|
6
6
|
requires_permission_to :view, :stuff, :except => [ :public ]
|
7
7
|
requires_permission_to :break, :stuff, :only => [ :dangerous ]
|
8
|
+
requires_permission_to :create, :stuff, :for => :account, :only => [ :create ]
|
8
9
|
|
9
10
|
def index; end
|
10
11
|
def public; end
|
11
12
|
def dangerous; end
|
13
|
+
def create; end
|
12
14
|
|
13
15
|
def rescue_action(e)
|
14
16
|
raise e
|
@@ -19,9 +21,14 @@ if defined?(ActionController)
|
|
19
21
|
credentials do |user|
|
20
22
|
user.can :view, :stuff
|
21
23
|
user.can :break, :stuff, :if => :special?
|
24
|
+
user.can :create, :stuff, :for => :account
|
25
|
+
|
26
|
+
def account; end
|
22
27
|
end
|
23
28
|
end
|
24
|
-
|
29
|
+
|
30
|
+
class TestAccount; end
|
31
|
+
|
25
32
|
describe TestController do
|
26
33
|
it "should know how to specify access credentials" do
|
27
34
|
controller.class.should respond_to(:requires_permission_to)
|
@@ -77,6 +84,27 @@ if defined?(ActionController)
|
|
77
84
|
}.should raise_error(Credentials::Errors::AccessDeniedError)
|
78
85
|
end
|
79
86
|
end
|
87
|
+
|
88
|
+
it "should be able to do stuff on its own account" do
|
89
|
+
@my_account = TestAccount.new
|
90
|
+
@user.stub!(:account).and_return(@my_account)
|
91
|
+
controller.stub!(:account).and_return(@my_account)
|
92
|
+
lambda {
|
93
|
+
get :create
|
94
|
+
response.should be_success
|
95
|
+
}.should_not raise_error(Credentials::Errors::AccessDeniedError)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should not be able to do stuff on someone else's account" do
|
99
|
+
@my_account = TestAccount.new
|
100
|
+
@your_account = TestAccount.new
|
101
|
+
@user.stub!(:account).and_return(@my_account)
|
102
|
+
controller.stub!(:account).and_return(@your_account)
|
103
|
+
lambda {
|
104
|
+
get :create
|
105
|
+
response.should_not be_success
|
106
|
+
}.should raise_error(Credentials::Errors::AccessDeniedError)
|
107
|
+
end
|
80
108
|
end
|
81
109
|
|
82
110
|
describe "when not logged in" do
|
data/spec/credentials_spec.rb
CHANGED
@@ -22,6 +22,14 @@ describe Animal do
|
|
22
22
|
it "should not be able to clean another animal" do
|
23
23
|
@sheep.should_not be_able_to :clean, @cow
|
24
24
|
end
|
25
|
+
|
26
|
+
it "should be able to laugh at its friends" do
|
27
|
+
@dog = Animal.new("Dog")
|
28
|
+
@dog.friends = [ @cow ]
|
29
|
+
|
30
|
+
@dog.should be_able_to :laugh, :at => @cow
|
31
|
+
@dog.should_not be_able_to :laugh, :at => @sheep
|
32
|
+
end
|
25
33
|
|
26
34
|
it "should have magic methods for permissions" do
|
27
35
|
lambda {
|
@@ -112,7 +120,7 @@ end
|
|
112
120
|
|
113
121
|
describe Man do
|
114
122
|
before :each do
|
115
|
-
@man = Man.new
|
123
|
+
@man = Man.new("Andrew")
|
116
124
|
end
|
117
125
|
|
118
126
|
it "should be able to do anything it wants" do
|
data/spec/domain.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
1
|
class Animal
|
2
2
|
credentials do |animal|
|
3
3
|
animal.can :clean, :self
|
4
|
+
animal.can :laugh, :at => :friend
|
4
5
|
end
|
5
6
|
|
6
7
|
attr_accessor :species, :hungry, :fast
|
8
|
+
attr_accessor :friends
|
9
|
+
def friends; @friends ||= []; end
|
7
10
|
|
8
11
|
def initialize(species = nil, hungry = false, fast = false)
|
9
12
|
@species = species
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: credentials
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Powell
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-12-16 00:00:00 +13:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|