Fix RfC Visibility to work as intended
The RfC Visibility of a consumer is intended to restrict who can access which RfCs. So far, those restrictions were only applied one way, for learners of a restricted consumer to view other (external) RfCs. However, the other way around should also work: If a RfC was created as part of a restricted consumer, no other external user should be able to interfere with this RfC. This commit, therefore, adds this direction as well and covers both directions with tests.
This commit is contained in:
@ -6,7 +6,7 @@ class RequestForCommentPolicy < ApplicationPolicy
|
|||||||
end
|
end
|
||||||
|
|
||||||
def show?
|
def show?
|
||||||
admin? || rfc_visibility
|
admin? || author? || rfc_visibility
|
||||||
end
|
end
|
||||||
|
|
||||||
def destroy?
|
def destroy?
|
||||||
@ -14,11 +14,11 @@ class RequestForCommentPolicy < ApplicationPolicy
|
|||||||
end
|
end
|
||||||
|
|
||||||
def mark_as_solved?
|
def mark_as_solved?
|
||||||
admin? || (author? && rfc_visibility)
|
admin? || author?
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_thank_you_note?
|
def set_thank_you_note?
|
||||||
admin? || (author? && rfc_visibility)
|
admin? || author?
|
||||||
end
|
end
|
||||||
|
|
||||||
def clear_question?
|
def clear_question?
|
||||||
@ -42,12 +42,16 @@ class RequestForCommentPolicy < ApplicationPolicy
|
|||||||
end
|
end
|
||||||
|
|
||||||
def rfc_visibility
|
def rfc_visibility
|
||||||
case @user.consumer.rfc_visibility
|
# The consumer with the most restricted visibility determines the visibility of the RfC
|
||||||
when 'all'
|
case [@user.consumer.rfc_visibility, @record.author.consumer.rfc_visibility]
|
||||||
|
# Only if both consumers allow learners to see all RfCs, the RfC is visible to the learner
|
||||||
|
when %w[all all]
|
||||||
everyone
|
everyone
|
||||||
when 'consumer'
|
# At least one consumer limits the visibility to the consumer
|
||||||
|
when %w[consumer all], %w[all consumer], %w[consumer consumer]
|
||||||
@record.author.consumer == @user.consumer
|
@record.author.consumer == @user.consumer
|
||||||
when 'study_group'
|
# At least one consumer limits the visibility to the study group
|
||||||
|
when %w[study_group all], %w[all study_group], %w[study_group consumer], %w[consumer study_group], %w[study_group study_group]
|
||||||
@record.submission.study_group.present? && @record.submission.study_group.id == @user.current_study_group_id
|
@record.submission.study_group.present? && @record.submission.study_group.id == @user.current_study_group_id
|
||||||
else
|
else
|
||||||
raise "Unknown RfC Visibility #{current_user.consumer.rfc_visibility}"
|
raise "Unknown RfC Visibility #{current_user.consumer.rfc_visibility}"
|
||||||
@ -61,8 +65,16 @@ class RequestForCommentPolicy < ApplicationPolicy
|
|||||||
else
|
else
|
||||||
case @user.consumer.rfc_visibility
|
case @user.consumer.rfc_visibility
|
||||||
when 'all'
|
when 'all'
|
||||||
@scope.all
|
# We need to filter those RfCs where the visibility is more restricted than the `all` visibility.
|
||||||
|
rfcs_with_users = @scope
|
||||||
|
.joins('LEFT OUTER JOIN external_users ON request_for_comments.user_type = \'ExternalUser\' AND request_for_comments.user_id = external_users.id')
|
||||||
|
.joins('LEFT OUTER JOIN internal_users ON request_for_comments.user_type = \'InternalUser\' AND request_for_comments.user_id = internal_users.id')
|
||||||
|
|
||||||
|
rfcs_with_users.where(external_users: {consumer_id: Consumer.rfc_visibility_all})
|
||||||
|
.or(rfcs_with_users.where(internal_users: {consumer_id: Consumer.rfc_visibility_all}))
|
||||||
when 'consumer'
|
when 'consumer'
|
||||||
|
# Since the `rfc_visibility` is set on a consumer level, we do not need to consider the `study_group` visibility here.
|
||||||
|
# Therefore, those RfCs where the author is limited to study group RfCs definitely belong to another consumer.
|
||||||
rfcs_with_users = @scope
|
rfcs_with_users = @scope
|
||||||
.joins('LEFT OUTER JOIN external_users ON request_for_comments.user_type = \'ExternalUser\' AND request_for_comments.user_id = external_users.id')
|
.joins('LEFT OUTER JOIN external_users ON request_for_comments.user_type = \'ExternalUser\' AND request_for_comments.user_id = external_users.id')
|
||||||
.joins('LEFT OUTER JOIN internal_users ON request_for_comments.user_type = \'InternalUser\' AND request_for_comments.user_id = internal_users.id')
|
.joins('LEFT OUTER JOIN internal_users ON request_for_comments.user_type = \'InternalUser\' AND request_for_comments.user_id = internal_users.id')
|
||||||
@ -70,6 +82,7 @@ class RequestForCommentPolicy < ApplicationPolicy
|
|||||||
rfcs_with_users.where(external_users: {consumer_id: @user.consumer.id})
|
rfcs_with_users.where(external_users: {consumer_id: @user.consumer.id})
|
||||||
.or(rfcs_with_users.where(internal_users: {consumer_id: @user.consumer.id}))
|
.or(rfcs_with_users.where(internal_users: {consumer_id: @user.consumer.id}))
|
||||||
when 'study_group'
|
when 'study_group'
|
||||||
|
# Since the `rfc_visibility` is already the most restricted visibility, we do not need to consider any other visibility here.
|
||||||
@scope
|
@scope
|
||||||
.joins(:submission)
|
.joins(:submission)
|
||||||
.where(submission: {study_group: @user.current_study_group_id})
|
.where(submission: {study_group: @user.current_study_group_id})
|
||||||
|
@ -4,7 +4,7 @@ FactoryBot.define do
|
|||||||
factory :rfc, class: 'RequestForComment' do
|
factory :rfc, class: 'RequestForComment' do
|
||||||
user factory: :external_user
|
user factory: :external_user
|
||||||
exercise factory: :math
|
exercise factory: :math
|
||||||
submission { association :submission, exercise:, user: }
|
submission { association :submission, exercise:, user:, study_group: user&.study_groups&.first }
|
||||||
file
|
file
|
||||||
sequence :question do |n|
|
sequence :question do |n|
|
||||||
"test question #{n}"
|
"test question #{n}"
|
||||||
|
@ -49,150 +49,245 @@ describe RequestForCommentPolicy do
|
|||||||
end
|
end
|
||||||
|
|
||||||
context 'when the RfC visibility is considered' do
|
context 'when the RfC visibility is considered' do
|
||||||
let(:user) { create(:learner, consumer:) }
|
shared_examples 'grants access to everyone' do
|
||||||
|
it 'grants access to everyone' do
|
||||||
|
%i[external_user teacher admin].each do |factory_name|
|
||||||
|
expect(policy).to permit(create(factory_name, consumer: viewer_consumer, study_groups: viewer_study_groups), rfc)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
let(:rfc_other_consumer) do
|
it 'grants access to authors' do
|
||||||
rfc = create(:rfc, user:)
|
expect(policy).to permit(rfc.author, rfc)
|
||||||
rfc.user.update(consumer: create(:consumer, name: 'Other Consumer'))
|
end
|
||||||
rfc
|
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:rfc_other_study_group) do
|
shared_examples 'grants access to admins and authors only' do
|
||||||
rfc = create(:rfc, user:)
|
it 'grants access to admins' do
|
||||||
rfc.user.update(consumer: user.consumer)
|
expect(policy).to permit(create(:admin, consumer: viewer_consumer, study_groups: viewer_study_groups), rfc)
|
||||||
rfc.submission.update(study_group: create(:study_group))
|
end
|
||||||
rfc
|
|
||||||
|
it 'grants access to authors' do
|
||||||
|
expect(policy).to permit(rfc.author, rfc)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'does not grant access to all other users' do
|
||||||
|
%i[external_user teacher].each do |factory_name|
|
||||||
|
expect(policy).not_to permit(create(factory_name, consumer: viewer_consumer, study_groups: viewer_study_groups), rfc)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
let(:rfc_peer) do
|
let(:rfc_author) { create(:learner, consumer: author_consumer, study_groups: author_study_groups) }
|
||||||
rfc = create(:rfc, user:)
|
let(:author_study_groups) { create_list(:study_group, 1, consumer: author_consumer) }
|
||||||
rfc.user.update(consumer: user.consumer)
|
let(:rfc) { create(:rfc, user: rfc_author) }
|
||||||
rfc.submission.update(study_group: user.study_groups.first)
|
|
||||||
rfc
|
|
||||||
end
|
|
||||||
|
|
||||||
let(:all_rfcs) { [rfc_other_consumer, rfc_other_study_group, rfc_peer] }
|
context "when the author's rfc_visibility is set to all" do
|
||||||
let(:same_consumer_rfcs) { [rfc_other_study_group, rfc_peer] }
|
let(:author_consumer) { create(:consumer, rfc_visibility: 'all') }
|
||||||
let(:other_study_groups_rfcs) { [rfc_other_consumer, rfc_other_study_group] }
|
|
||||||
|
|
||||||
context 'when rfc_visibility is set to all' do
|
context 'when the viewer is from another consumer' do
|
||||||
let(:consumer) { create(:consumer, rfc_visibility: 'all') }
|
context "when the viewer's rfc_visibility is set to all" do
|
||||||
|
let(:viewer_consumer) { create(:consumer, name: 'Other Consumer', rfc_visibility: 'all') }
|
||||||
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
|
|
||||||
permissions(:show?) do
|
permissions(:show?) do
|
||||||
it 'grants access to everyone' do
|
include_examples 'grants access to everyone'
|
||||||
%i[external_user teacher admin].each do |factory_name|
|
end
|
||||||
all_rfcs.each do |specific_rfc|
|
|
||||||
expect(policy).to permit(create(factory_name, consumer:), specific_rfc)
|
%i[mark_as_solved? set_thank_you_note?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the viewer's rfc_visibility is set to consumer" do
|
||||||
|
let(:viewer_consumer) { create(:consumer, name: 'Other Consumer', rfc_visibility: 'consumer') }
|
||||||
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note? show?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the viewer's rfc_visibility is set to study_group" do
|
||||||
|
let(:viewer_consumer) { create(:consumer, name: 'Other Consumer', rfc_visibility: 'study_group') }
|
||||||
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note? show?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
%i[mark_as_solved? set_thank_you_note?].each do |action|
|
context 'when the viewer is from the same consumer' do
|
||||||
permissions(action) do
|
let(:viewer_consumer) { author_consumer }
|
||||||
it 'grants access to admins' do
|
|
||||||
all_rfcs.each do |specific_rfc|
|
context 'when the viewer is from another study group' do
|
||||||
expect(policy).to permit(create(:admin, consumer:), specific_rfc)
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
end
|
|
||||||
|
permissions(:show?) do
|
||||||
|
include_examples 'grants access to everyone'
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'grants access to authors' do
|
%i[mark_as_solved? set_thank_you_note?].each do |action|
|
||||||
all_rfcs.each do |specific_rfc|
|
permissions(action) do
|
||||||
expect(policy).to permit(specific_rfc.author, specific_rfc)
|
include_examples 'grants access to admins and authors only'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
it 'does not grant access to all other users' do
|
context 'when the viewer is from the same study group' do
|
||||||
%i[external_user teacher].each do |factory_name|
|
let(:viewer_study_groups) { author_study_groups }
|
||||||
all_rfcs.each do |specific_rfc|
|
|
||||||
expect(policy).not_to permit(create(factory_name, consumer:), specific_rfc)
|
permissions(:show?) do
|
||||||
end
|
include_examples 'grants access to everyone'
|
||||||
|
end
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when rfc_visibility is set to consumer' do
|
context "when the author's rfc_visibility is set to consumer" do
|
||||||
let(:consumer) { create(:consumer, rfc_visibility: 'consumer') }
|
let(:author_consumer) { create(:consumer, rfc_visibility: 'consumer') }
|
||||||
|
|
||||||
permissions(:show?) do
|
context 'when the viewer is from another consumer' do
|
||||||
it 'grants access to admins' do
|
context "when the viewer's rfc_visibility is set to all" do
|
||||||
all_rfcs.each do |specific_rfc|
|
let(:viewer_consumer) { create(:consumer, name: 'Other Consumer', rfc_visibility: 'all') }
|
||||||
expect(policy).to permit(create(:admin, consumer:), specific_rfc)
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'grants access to users from the same consumer' do
|
%i[mark_as_solved? set_thank_you_note? show?].each do |action|
|
||||||
%i[external_user teacher].each do |factory_name|
|
permissions(action) do
|
||||||
same_consumer_rfcs.each do |specific_rfc|
|
include_examples 'grants access to admins and authors only'
|
||||||
expect(policy).to permit(create(factory_name, consumer:), specific_rfc)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not grant access to users from other consumers' do
|
context "when the viewer's rfc_visibility is set to consumer" do
|
||||||
%i[external_user teacher].each do |factory_name|
|
let(:viewer_consumer) { create(:consumer, name: 'Other Consumer', rfc_visibility: 'consumer') }
|
||||||
expect(policy).not_to permit(create(factory_name, consumer:), rfc_other_consumer)
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note? show?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "when the viewer's rfc_visibility is set to study_group" do
|
||||||
|
let(:viewer_consumer) { create(:consumer, name: 'Other Consumer', rfc_visibility: 'study_group') }
|
||||||
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note? show?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Testing `mark_as_solved?` and `set_thank_you_note?` is not necessary here,
|
context 'when the viewer is from the same consumer' do
|
||||||
# because an author of an RfC can only be a user from the same consumer.
|
let(:viewer_consumer) { author_consumer }
|
||||||
|
|
||||||
|
context 'when the viewer is from another study group' do
|
||||||
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
|
|
||||||
|
permissions(:show?) do
|
||||||
|
include_examples 'grants access to everyone'
|
||||||
|
end
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when the viewer is from the same study group' do
|
||||||
|
let(:viewer_study_groups) { author_study_groups }
|
||||||
|
|
||||||
|
permissions(:show?) do
|
||||||
|
include_examples 'grants access to everyone'
|
||||||
|
end
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when rfc_visibility is set to study_group' do
|
context "when the author's rfc_visibility is set to study_group" do
|
||||||
let(:consumer) { create(:consumer, rfc_visibility: 'study_group') }
|
let(:author_consumer) { create(:consumer, rfc_visibility: 'study_group') }
|
||||||
|
|
||||||
permissions(:show?) do
|
context 'when the viewer is from another consumer' do
|
||||||
it 'grants access to admins' do
|
context "when the viewer's rfc_visibility is set to all" do
|
||||||
all_rfcs.each do |specific_rfc|
|
let(:viewer_consumer) { create(:consumer, name: 'Other Consumer', rfc_visibility: 'all') }
|
||||||
expect(policy).to permit(create(:admin, consumer:), specific_rfc)
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note? show?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'grants access to users from the same study group' do
|
context "when the viewer's rfc_visibility is set to consumer" do
|
||||||
%i[external_user teacher].each do |factory_name|
|
let(:viewer_consumer) { create(:consumer, name: 'Other Consumer', rfc_visibility: 'consumer') }
|
||||||
expect(policy).to permit(create(factory_name, consumer:, study_groups: [rfc_peer.submission.study_group]), rfc_peer)
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note? show?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not grant access to users from other consumers' do
|
context "when the viewer's rfc_visibility is set to study_group" do
|
||||||
%i[external_user teacher].each do |factory_name|
|
let(:viewer_consumer) { create(:consumer, name: 'Other Consumer', rfc_visibility: 'study_group') }
|
||||||
other_study_groups_rfcs.each do |specific_rfc|
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
expect(policy).not_to permit(create(factory_name, consumer:), specific_rfc)
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note? show?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
%i[mark_as_solved? set_thank_you_note?].each do |action|
|
context 'when the viewer is from the same consumer' do
|
||||||
permissions(action) do
|
let(:viewer_consumer) { author_consumer }
|
||||||
it 'grants access to admins' do
|
|
||||||
all_rfcs.each do |specific_rfc|
|
context 'when the viewer is from another study group' do
|
||||||
expect(policy).to permit(create(:admin, consumer:), specific_rfc)
|
let(:viewer_study_groups) { create_list(:study_group, 1, consumer: viewer_consumer) }
|
||||||
|
|
||||||
|
%i[mark_as_solved? set_thank_you_note? show?].each do |action|
|
||||||
|
permissions(action) do
|
||||||
|
include_examples 'grants access to admins and authors only'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
# Testing `mark_as_solved?` and `set_thank_you_note?` with another consumer is not
|
context 'when the viewer is from the same study group' do
|
||||||
# necessary here, because an author of an RfC can only be a user from the same consumer.
|
let(:viewer_study_groups) { author_study_groups }
|
||||||
|
|
||||||
it 'grants access to authors of the same primary study group' do
|
permissions(:show?) do
|
||||||
rfc_peer.author.update(study_groups: [rfc_peer.submission.study_group])
|
include_examples 'grants access to everyone'
|
||||||
expect(policy).to permit(rfc_peer.author, rfc_peer)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'does not grant access to authors of another primary study groups' do
|
%i[mark_as_solved? set_thank_you_note?].each do |action|
|
||||||
rfc_other_study_group.author.update(study_groups: create_list(:study_group, 1))
|
permissions(action) do
|
||||||
expect(policy).not_to permit(rfc_other_study_group.author, rfc_other_study_group)
|
include_examples 'grants access to admins and authors only'
|
||||||
end
|
|
||||||
|
|
||||||
it 'does not grant access to all other users' do
|
|
||||||
%i[external_user teacher].each do |factory_name|
|
|
||||||
all_rfcs.each do |specific_rfc|
|
|
||||||
expect(policy).not_to permit(create(factory_name, consumer:), specific_rfc)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user