credentials 2.2.3 → 2.3.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/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