Skip to content

Commit 03dcfd2

Browse files
Support strict with_role queries. Issue #362 (#543)
* Support strict with_role queries Why: * There is an outstanding issues #362 * strict mode should be enforced on role queries and resource queries How: * Pass a strict flag to the scope function * call where or where_strict depending on strict flag in active_record adapter * Add tests for strict mode resource queries Note: * This PR does not address strict mode for mongoid since I am unfamiliar with that ORM * Update where_strict for mongoid Why: * The tests will fail until mongo works with strict resource querying This change addresses the need by: * Copy the where_strict implementation from the active record adapter * Make sure to call strict where * Refactor to reduce mutations Co-authored-by: Thomas McDonald <thomas-mcdonald@users.noreply.github.com>
1 parent 8c3e829 commit 03dcfd2

File tree

4 files changed

+89
-53
lines changed

4 files changed

+89
-53
lines changed

lib/rolify/adapters/active_record/role_adapter.rb

+16-10
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,20 @@ def where(relation, *args)
99
end
1010

1111
def where_strict(relation, args)
12-
return relation.where(:name => args[:name]) if args[:resource].blank?
13-
resource = if args[:resource].is_a?(Class)
14-
{class: args[:resource].to_s, id: nil}
15-
else
16-
{class: args[:resource].class.name, id: args[:resource].id}
17-
end
18-
19-
relation.where(:name => args[:name], :resource_type => resource[:class], :resource_id => resource[:id])
12+
wrap_conditions = relation.name != role_class.name
13+
14+
conditions = if args[:resource].is_a?(Class)
15+
{:resource_type => args[:resource].to_s, :resource_id => nil }
16+
elsif args[:resource].present?
17+
{:resource_type => args[:resource].class.name, :resource_id => args[:resource].id}
18+
else
19+
{}
20+
end
21+
22+
conditions.merge!(:name => args[:name])
23+
conditions = wrap_conditions ? { role_table => conditions } : conditions
24+
25+
relation.where(conditions)
2026
end
2127

2228
def find_cached(relation, args)
@@ -67,9 +73,9 @@ def exists?(relation, column)
6773
relation.where("#{column} IS NOT NULL")
6874
end
6975

70-
def scope(relation, conditions)
76+
def scope(relation, conditions, strict)
7177
query = relation.joins(:roles)
72-
query = where(query, conditions)
78+
query = strict ? where_strict(query, conditions) : where(query, conditions)
7379
query
7480
end
7581

lib/rolify/adapters/mongoid/role_adapter.rb

+17-10
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,20 @@ def where(relation, *args)
99
end
1010

1111
def where_strict(relation, args)
12-
return relation.where(:name => args[:name]) if args[:resource].blank?
13-
resource = if args[:resource].is_a?(Class)
14-
{class: args[:resource].to_s, id: nil}
15-
else
16-
{class: args[:resource].class.name, id: args[:resource].id}
17-
end
18-
19-
relation.where(:name => args[:name], :resource_type => resource[:class], :resource_id => resource[:id])
12+
wrap_conditions = relation.name != role_class.name
13+
14+
conditions = if args[:resource].is_a?(Class)
15+
{:resource_type => args[:resource].to_s, :resource_id => nil }
16+
elsif args[:resource].present?
17+
{:resource_type => args[:resource].class.name, :resource_id => args[:resource].id}
18+
else
19+
{}
20+
end
21+
22+
conditions.merge!(:name => args[:name])
23+
conditions = wrap_conditions ? { role_table => conditions } : conditions
24+
25+
relation.where(conditions)
2026
end
2127

2228
def find_cached(relation, args)
@@ -84,8 +90,9 @@ def exists?(relation, column)
8490
relation.where(column.to_sym.ne => nil)
8591
end
8692

87-
def scope(relation, conditions)
88-
roles = where(role_class, conditions).map { |role| role.id }
93+
def scope(relation, conditions, strict)
94+
query = strict ? where_strict(role_class, conditions) : where(role_class, conditions)
95+
roles = query.map { |role| role.id }
8996
return [] if roles.size.zero?
9097
query = relation.any_in(:role_ids => roles)
9198
query

lib/rolify/finders.rb

+6-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
11
module Rolify
22
module Finders
33
def with_role(role_name, resource = nil)
4-
self.adapter.scope(self, :name => role_name, :resource => resource)
4+
strict = self.strict_rolify and resource and resource != :any
5+
self.adapter.scope(
6+
self,
7+
{ :name => role_name, :resource => resource },
8+
strict
9+
)
510
end
611

712
def without_role(role_name, resource = nil)

spec/rolify/shared_examples/shared_examples_for_finders.rb

+50-32
Original file line numberDiff line numberDiff line change
@@ -4,47 +4,65 @@
44
it { should respond_to(:with_role).with(1).argument }
55
it { should respond_to(:with_role).with(2).arguments }
66

7-
context "with a global role" do
8-
it { subject.with_role("admin".send(param_method)).should eq([ root ]) }
9-
it { subject.with_role("moderator".send(param_method)).should be_empty }
10-
it { subject.with_role("visitor".send(param_method)).should be_empty }
11-
end
12-
13-
context "with a class scoped role" do
14-
context "on Forum class" do
15-
it { subject.with_role("admin".send(param_method), Forum).should eq([ root ]) }
16-
it { subject.with_role("moderator".send(param_method), Forum).should eq([ modo ]) }
17-
it { subject.with_role("visitor".send(param_method), Forum).should be_empty }
7+
context "when resource setting: strict is set to false" do
8+
context "with a global role" do
9+
it { subject.with_role("admin".send(param_method)).should eq([ root ]) }
10+
it { subject.with_role("moderator".send(param_method)).should be_empty }
11+
it { subject.with_role("visitor".send(param_method)).should be_empty }
1812
end
1913

20-
context "on Group class" do
21-
it { subject.with_role("admin".send(param_method), Group).should eq([ root ]) }
22-
it { subject.with_role("moderator".send(param_method), Group).should eq([ root ]) }
23-
it { subject.with_role("visitor".send(param_method), Group).should be_empty }
14+
context "with a class scoped role" do
15+
context "on Forum class" do
16+
it { subject.with_role("admin".send(param_method), Forum).should eq([ root ]) }
17+
it { subject.with_role("moderator".send(param_method), Forum).should eq([ modo ]) }
18+
it { subject.with_role("visitor".send(param_method), Forum).should be_empty }
19+
end
20+
21+
context "on Group class" do
22+
it { subject.with_role("admin".send(param_method), Group).should eq([ root ]) }
23+
it { subject.with_role("moderator".send(param_method), Group).should eq([ root ]) }
24+
it { subject.with_role("visitor".send(param_method), Group).should be_empty }
25+
end
2426
end
25-
end
2627

27-
context "with an instance scoped role" do
28-
context "on Forum.first instance" do
29-
it { subject.with_role("admin".send(param_method), Forum.first).should eq([ root ]) }
30-
it { subject.with_role("moderator".send(param_method), Forum.first).should eq([ modo ]) }
31-
it { subject.with_role("visitor".send(param_method), Forum.first).should be_empty }
28+
context "with an instance scoped role" do
29+
context "on Forum.first instance" do
30+
it { subject.with_role("admin".send(param_method), Forum.first).should eq([ root ]) }
31+
it { subject.with_role("moderator".send(param_method), Forum.first).should eq([ modo ]) }
32+
it { subject.with_role("visitor".send(param_method), Forum.first).should be_empty }
33+
end
34+
35+
context "on Forum.last instance" do
36+
it { subject.with_role("admin".send(param_method), Forum.last).should eq([ root ]) }
37+
it { subject.with_role("moderator".send(param_method), Forum.last).should eq([ modo ]) }
38+
it { subject.with_role("visitor".send(param_method), Forum.last).should include(root, visitor) } # =~ doesn't pass using mongoid, don't know why...
39+
end
40+
41+
context "on Group.first instance" do
42+
it { subject.with_role("admin".send(param_method), Group.first).should eq([ root ]) }
43+
it { subject.with_role("moderator".send(param_method), Group.first).should eq([ root ]) }
44+
it { subject.with_role("visitor".send(param_method), Group.first).should eq([ modo ]) }
45+
end
46+
47+
context "on Company.first_instance" do
48+
it { subject.with_role("owner".send(param_method), Company.first).should eq([ owner ]) }
49+
end
3250
end
51+
end
3352

34-
context "on Forum.last instance" do
35-
it { subject.with_role("admin".send(param_method), Forum.last).should eq([ root ]) }
36-
it { subject.with_role("moderator".send(param_method), Forum.last).should eq([ modo ]) }
37-
it { subject.with_role("visitor".send(param_method), Forum.last).should include(root, visitor) } # =~ doesn't pass using mongoid, don't know why...
53+
context "when resource setting: strict is set to true" do
54+
before(:context) do
55+
user_class.strict_rolify = true
3856
end
39-
40-
context "on Group.first instance" do
41-
it { subject.with_role("admin".send(param_method), Group.first).should eq([ root ]) }
42-
it { subject.with_role("moderator".send(param_method), Group.first).should eq([ root ]) }
43-
it { subject.with_role("visitor".send(param_method), Group.first).should eq([ modo ]) }
57+
after(:context) do
58+
user_class.strict_rolify = false
4459
end
4560

46-
context "on Company.first_instance" do
47-
it { subject.with_role("owner".send(param_method), Company.first).should eq([ owner ]) }
61+
context "with an instance scoped role" do
62+
context "on Forum.first instance" do
63+
it { subject.with_role("admin".send(param_method), Forum.first).should be_empty }
64+
it { subject.with_role("moderator".send(param_method), Forum.first).should be_empty }
65+
end
4866
end
4967
end
5068
end

0 commit comments

Comments
 (0)