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 CHANGED
@@ -1,5 +1,9 @@
1
1
  === 2.2.3 / 2009-11-27
2
2
 
3
+ * Add support for prepositions.
4
+
5
+ === 2.2.3 / 2009-11-27
6
+
3
7
  * Minor fix to ActionController extension.
4
8
 
5
9
  === 2.2.2 / 2009-11-25
data/VERSION CHANGED
@@ -1 +1 @@
1
- 2.2.3
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.2.3"
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-11-27}
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 : {}).with_indifferent_access
51
- %w(only except).each do |key|
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
@@ -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
- return false unless arity == args.length
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
@@ -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
@@ -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.2.3
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-11-27 00:00:00 +13:00
12
+ date: 2009-12-16 00:00:00 +13:00
13
13
  default_executable:
14
14
  dependencies: []
15
15