Add consumer-based RfC Visibility settings
This setting will be useful to increase data protection, where users might not be allowed to see RfCs from other contexts.
This commit is contained in:
@ -23,7 +23,7 @@ class ConsumersController < ApplicationController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def consumer_params
|
def consumer_params
|
||||||
params[:consumer].permit(:name, :oauth_key, :oauth_secret) if params[:consumer].present?
|
params[:consumer].permit(:name, :oauth_key, :oauth_secret, :rfc_visibility) if params[:consumer].present?
|
||||||
end
|
end
|
||||||
private :consumer_params
|
private :consumer_params
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ class RequestForCommentsController < ApplicationController
|
|||||||
# GET /request_for_comments
|
# GET /request_for_comments
|
||||||
# GET /request_for_comments.json
|
# GET /request_for_comments.json
|
||||||
def index
|
def index
|
||||||
@search = RequestForComment
|
@search = policy_scope(RequestForComment)
|
||||||
.last_per_user(2)
|
.last_per_user(2)
|
||||||
.with_last_activity
|
.with_last_activity
|
||||||
.ransack(params[:q])
|
.ransack(params[:q])
|
||||||
@ -31,7 +31,7 @@ class RequestForCommentsController < ApplicationController
|
|||||||
|
|
||||||
# GET /my_request_for_comments
|
# GET /my_request_for_comments
|
||||||
def my_comment_requests
|
def my_comment_requests
|
||||||
@search = RequestForComment
|
@search = policy_scope(RequestForComment)
|
||||||
.with_last_activity
|
.with_last_activity
|
||||||
.where(user: current_user)
|
.where(user: current_user)
|
||||||
.ransack(params[:q])
|
.ransack(params[:q])
|
||||||
@ -44,7 +44,7 @@ class RequestForCommentsController < ApplicationController
|
|||||||
|
|
||||||
# GET /my_rfc_activity
|
# GET /my_rfc_activity
|
||||||
def rfcs_with_my_comments
|
def rfcs_with_my_comments
|
||||||
@search = RequestForComment
|
@search = policy_scope(RequestForComment)
|
||||||
.with_last_activity
|
.with_last_activity
|
||||||
.joins(:comments) # we don't need to outer join here, because we know the user has commented on these
|
.joins(:comments) # we don't need to outer join here, because we know the user has commented on these
|
||||||
.where(comments: {user_id: current_user.id})
|
.where(comments: {user_id: current_user.id})
|
||||||
@ -59,7 +59,7 @@ class RequestForCommentsController < ApplicationController
|
|||||||
# GET /exercises/:id/request_for_comments
|
# GET /exercises/:id/request_for_comments
|
||||||
def rfcs_for_exercise
|
def rfcs_for_exercise
|
||||||
exercise = Exercise.find(params[:exercise_id])
|
exercise = Exercise.find(params[:exercise_id])
|
||||||
@search = RequestForComment
|
@search = policy_scope(RequestForComment)
|
||||||
.with_last_activity
|
.with_last_activity
|
||||||
.where(exercise_id: exercise.id)
|
.where(exercise_id: exercise.id)
|
||||||
.ransack(params[:q])
|
.ransack(params[:q])
|
||||||
@ -155,8 +155,14 @@ class RequestForCommentsController < ApplicationController
|
|||||||
# The study groups are grouped by the current study group and other study groups of the user
|
# The study groups are grouped by the current study group and other study groups of the user
|
||||||
def set_study_group_grouping
|
def set_study_group_grouping
|
||||||
current_study_group = StudyGroup.find_by(id: session[:study_group_id])
|
current_study_group = StudyGroup.find_by(id: session[:study_group_id])
|
||||||
my_study_groups = current_user.study_groups.reject {|group| group == current_study_group }
|
my_study_groups = case current_user.consumer.rfc_visibility
|
||||||
|
when 'all' then current_user.study_groups.order(name: :desc)
|
||||||
|
when 'consumer' then current_user.study_groups.where(consumer: current_user.consumer).order(name: :desc)
|
||||||
|
when 'study_group' then current_study_group.present? ? Array(current_study_group) : []
|
||||||
|
else raise "Unknown RfC Visibility #{current_user.consumer.rfc_visibility}"
|
||||||
|
end
|
||||||
|
|
||||||
@study_groups_grouping = [[t('request_for_comments.index.study_groups.current'), Array(current_study_group)],
|
@study_groups_grouping = [[t('request_for_comments.index.study_groups.current'), Array(current_study_group)],
|
||||||
[t('request_for_comments.index.study_groups.my'), my_study_groups]]
|
[t('request_for_comments.index.study_groups.my'), my_study_groups.reject {|group| group == current_study_group }]]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,6 +1,12 @@
|
|||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class Consumer < ApplicationRecord
|
class Consumer < ApplicationRecord
|
||||||
|
enum rfc_visibility: {
|
||||||
|
all: 0,
|
||||||
|
consumer: 1,
|
||||||
|
study_group: 2,
|
||||||
|
}, _default: :all, _prefix: true
|
||||||
|
|
||||||
has_many :users
|
has_many :users
|
||||||
has_many :study_groups, dependent: :destroy
|
has_many :study_groups, dependent: :destroy
|
||||||
|
|
||||||
|
@ -88,7 +88,20 @@ class RequestForComment < ApplicationRecord
|
|||||||
private
|
private
|
||||||
|
|
||||||
def row_number_user_sql
|
def row_number_user_sql
|
||||||
select('id, user_id, user_type, exercise_id, file_id, question, created_at, updated_at, solved, full_score_reached, submission_id, row_number() OVER (PARTITION BY user_id, user_type ORDER BY created_at DESC) as row_number')
|
select('
|
||||||
|
request_for_comments.id,
|
||||||
|
request_for_comments.user_id,
|
||||||
|
request_for_comments.user_type,
|
||||||
|
request_for_comments.exercise_id,
|
||||||
|
request_for_comments.file_id,
|
||||||
|
request_for_comments.question,
|
||||||
|
request_for_comments.created_at,
|
||||||
|
request_for_comments.updated_at,
|
||||||
|
request_for_comments.solved,
|
||||||
|
request_for_comments.full_score_reached,
|
||||||
|
request_for_comments.submission_id,
|
||||||
|
row_number() OVER (PARTITION BY request_for_comments.user_id, request_for_comments.user_type ORDER BY request_for_comments.created_at DESC) as row_number
|
||||||
|
')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -48,4 +48,30 @@ class RequestForCommentPolicy < ApplicationPolicy
|
|||||||
def rfcs_with_my_comments?
|
def rfcs_with_my_comments?
|
||||||
everyone
|
everyone
|
||||||
end
|
end
|
||||||
|
|
||||||
|
class Scope < Scope
|
||||||
|
def resolve
|
||||||
|
if @user.admin?
|
||||||
|
@scope.all
|
||||||
|
else
|
||||||
|
case @user.consumer.rfc_visibility
|
||||||
|
when 'all'
|
||||||
|
@scope.all
|
||||||
|
when 'consumer'
|
||||||
|
rfcs_with_users = @scope.distinct
|
||||||
|
.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: @user.consumer.id})
|
||||||
|
.or(rfcs_with_users.where(internal_users: {consumer_id: @user.consumer.id}))
|
||||||
|
when 'study_group'
|
||||||
|
@scope.distinct
|
||||||
|
.joins(:submission)
|
||||||
|
.where(submission: {study_group: @user.current_study_group_id})
|
||||||
|
else
|
||||||
|
@scope.none
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
@ -9,4 +9,7 @@
|
|||||||
.mb-3
|
.mb-3
|
||||||
= f.label(:oauth_secret, class: 'form-label')
|
= f.label(:oauth_secret, class: 'form-label')
|
||||||
= f.text_field(:oauth_secret, class: 'form-control', required: true)
|
= f.text_field(:oauth_secret, class: 'form-control', required: true)
|
||||||
|
.mb-3
|
||||||
|
= f.label(:rfc_visibility, class: 'form-label')
|
||||||
|
= f.collection_select(:rfc_visibility, Consumer.rfc_visibilities.map { |rfc_visibility, _id| [t("activerecord.attributes.consumer.rfc_visibility_type.#{rfc_visibility}"), rfc_visibility] }, :second, :first, {}, class: 'form-control form-control-sm')
|
||||||
.actions = render('shared/submit_button', f: f, object: @consumer)
|
.actions = render('shared/submit_button', f: f, object: @consumer)
|
||||||
|
@ -6,5 +6,6 @@ h1
|
|||||||
- %w[oauth_key oauth_secret].each do |attribute|
|
- %w[oauth_key oauth_secret].each do |attribute|
|
||||||
= row(label: "consumer.#{attribute}") do
|
= row(label: "consumer.#{attribute}") do
|
||||||
= content_tag(:input, nil, class: 'form-control bg-secondary', readonly: true, value: @consumer.send(attribute))
|
= content_tag(:input, nil, class: 'form-control bg-secondary', readonly: true, value: @consumer.send(attribute))
|
||||||
|
= row(label: 'consumer.rfc_visibility', value: t("activerecord.attributes.consumer.rfc_visibility_type.#{@consumer.rfc_visibility}"))
|
||||||
|
|
||||||
= render('study_groups/table', study_groups: @consumer.study_groups.sort)
|
= render('study_groups/table', study_groups: @consumer.study_groups.sort)
|
||||||
|
@ -9,11 +9,12 @@ h1 = RequestForComment.model_name.human(count: 2)
|
|||||||
.col-auto.mt-3.mt-md-0
|
.col-auto.mt-3.mt-md-0
|
||||||
= f.label(:title_cont, t('request_for_comments.solved'), class: 'visually-hidden form-label')
|
= f.label(:title_cont, t('request_for_comments.solved'), class: 'visually-hidden form-label')
|
||||||
= f.select(:solved_not_eq, [[t('request_for_comments.show_all'), 2], [t('request_for_comments.show_unsolved'), 1], [t('request_for_comments.show_solved'), 0]])
|
= f.select(:solved_not_eq, [[t('request_for_comments.show_all'), 2], [t('request_for_comments.show_unsolved'), 1], [t('request_for_comments.show_solved'), 0]])
|
||||||
.row
|
- unless current_user.consumer.rfc_visibility_study_group?
|
||||||
.col
|
.row
|
||||||
= f.label(:submission_study_group_id_eq, t('request_for_comments.index.study_groups.placeholder'), class: 'visually-hidden form-label')
|
.col
|
||||||
= f.grouped_collection_select(:submission_study_group_id_in, @study_groups_grouping, :second, :first, :id, :to_s, {},
|
= f.label(:submission_study_group_id_eq, t('request_for_comments.index.study_groups.placeholder'), class: 'visually-hidden form-label')
|
||||||
{ class: 'form-control', multiple: true, "data-placeholder": t('request_for_comments.index.study_groups.placeholder') })
|
= f.grouped_collection_select(:submission_study_group_id_in, @study_groups_grouping, :second, :first, :id, :to_s, {},
|
||||||
|
{ class: 'form-control', multiple: true, "data-placeholder": t('request_for_comments.index.study_groups.placeholder') })
|
||||||
|
|
||||||
.table-responsive
|
.table-responsive
|
||||||
table.table.sortable.mt-4
|
table.table.sortable.mt-4
|
||||||
|
@ -7,6 +7,11 @@ de:
|
|||||||
name: Name
|
name: Name
|
||||||
oauth_key: OAuth Key
|
oauth_key: OAuth Key
|
||||||
oauth_secret: OAuth Secret
|
oauth_secret: OAuth Secret
|
||||||
|
rfc_visibility: Sichtbarkeit von Kommentaranfragen
|
||||||
|
rfc_visibility_type:
|
||||||
|
all: Alle Kommentaranfrage sind sichtbar
|
||||||
|
consumer: Nur Kommentaranfragen des aktuellen Konsumenten sind sichtbar
|
||||||
|
study_group: Nur Kommentaranfragen der aktuellen Lerngruppe sind sichtbar
|
||||||
execution_environment:
|
execution_environment:
|
||||||
docker_image: Docker-Image
|
docker_image: Docker-Image
|
||||||
exposed_ports: Zugängliche Ports
|
exposed_ports: Zugängliche Ports
|
||||||
|
@ -7,6 +7,11 @@ en:
|
|||||||
name: Name
|
name: Name
|
||||||
oauth_key: OAuth Key
|
oauth_key: OAuth Key
|
||||||
oauth_secret: OAuth Secret
|
oauth_secret: OAuth Secret
|
||||||
|
rfc_visibility: Visibility of Request for Comments
|
||||||
|
rfc_visibility_type:
|
||||||
|
all: All Requests for Comments are visible
|
||||||
|
consumer: Only Requests for Comments of the current consumer are visible
|
||||||
|
study_group: Only Requests for Comments of the current study group are visible
|
||||||
execution_environment:
|
execution_environment:
|
||||||
docker_image: Docker Image
|
docker_image: Docker Image
|
||||||
exposed_ports: Exposed Ports
|
exposed_ports: Exposed Ports
|
||||||
|
@ -0,0 +1,7 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class AddRfcVisibilityToConsumer < ActiveRecord::Migration[7.0]
|
||||||
|
def change
|
||||||
|
add_column :consumers, :rfc_visibility, :integer, limit: 1, null: false, default: 0, comment: 'Used as enum in Rails'
|
||||||
|
end
|
||||||
|
end
|
@ -10,7 +10,7 @@
|
|||||||
#
|
#
|
||||||
# It's strongly recommended that you check this file into your version control system.
|
# It's strongly recommended that you check this file into your version control system.
|
||||||
|
|
||||||
ActiveRecord::Schema[7.0].define(version: 2023_02_06_203117) do
|
ActiveRecord::Schema[7.0].define(version: 2023_02_19_113125) do
|
||||||
# These are extensions that must be enabled in order to support this database
|
# These are extensions that must be enabled in order to support this database
|
||||||
enable_extension "pg_trgm"
|
enable_extension "pg_trgm"
|
||||||
enable_extension "pgcrypto"
|
enable_extension "pgcrypto"
|
||||||
@ -107,6 +107,7 @@ ActiveRecord::Schema[7.0].define(version: 2023_02_06_203117) do
|
|||||||
t.datetime "updated_at"
|
t.datetime "updated_at"
|
||||||
t.string "oauth_key"
|
t.string "oauth_key"
|
||||||
t.string "oauth_secret"
|
t.string "oauth_secret"
|
||||||
|
t.integer "rfc_visibility", limit: 2, default: 0, null: false, comment: "Used as enum in Rails"
|
||||||
end
|
end
|
||||||
|
|
||||||
create_table "error_template_attributes", id: :serial, force: :cascade do |t|
|
create_table "error_template_attributes", id: :serial, force: :cascade do |t|
|
||||||
|
@ -9,6 +9,57 @@ describe RequestForCommentsController do
|
|||||||
|
|
||||||
before { allow(controller).to receive(:current_user).and_return(user) }
|
before { allow(controller).to receive(:current_user).and_return(user) }
|
||||||
|
|
||||||
|
shared_examples 'RfC visibility settings' do
|
||||||
|
let(:user) { create(:learner) }
|
||||||
|
|
||||||
|
let!(:rfc_other_consumer) do
|
||||||
|
rfc = create(:rfc)
|
||||||
|
rfc.user.update(consumer: create(:consumer, name: 'Other Consumer'))
|
||||||
|
rfc
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:rfc_other_study_group) do
|
||||||
|
rfc = create(:rfc)
|
||||||
|
rfc.user.update(consumer: user.consumer)
|
||||||
|
rfc.submission.update(study_group: create(:study_group))
|
||||||
|
rfc
|
||||||
|
end
|
||||||
|
|
||||||
|
let!(:rfc_peer) do
|
||||||
|
rfc = create(:rfc)
|
||||||
|
rfc.user.update(consumer: user.consumer)
|
||||||
|
rfc.submission.update(study_group: user.study_groups.first)
|
||||||
|
rfc
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when rfc_visibility is set to all' do
|
||||||
|
before { user.consumer.update(rfc_visibility: 'all') }
|
||||||
|
|
||||||
|
it 'shows all RfCs' do
|
||||||
|
get :index
|
||||||
|
expect(assigns(:request_for_comments)).to match_array([rfc_other_consumer, rfc_other_study_group, rfc_peer])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when rfc_visibility is set to consumer' do
|
||||||
|
before { user.consumer.update(rfc_visibility: 'consumer') }
|
||||||
|
|
||||||
|
it 'shows only RfCs of the same consumer' do
|
||||||
|
get :index
|
||||||
|
expect(assigns(:request_for_comments)).to match_array([rfc_other_study_group, rfc_peer])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when rfc_visibility is set to study_group' do
|
||||||
|
before { user.consumer.update(rfc_visibility: 'study_group') }
|
||||||
|
|
||||||
|
it 'shows only RfCs of the same study group' do
|
||||||
|
get :index
|
||||||
|
expect(assigns(:request_for_comments)).to match_array([rfc_peer])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
describe 'GET #index' do
|
describe 'GET #index' do
|
||||||
it 'renders the index template' do
|
it 'renders the index template' do
|
||||||
get :index
|
get :index
|
||||||
@ -32,6 +83,8 @@ describe RequestForCommentsController do
|
|||||||
|
|
||||||
expect(assigns(:request_for_comments)).to eq([rfc_within_my_study_group])
|
expect(assigns(:request_for_comments)).to eq([rfc_within_my_study_group])
|
||||||
end
|
end
|
||||||
|
|
||||||
|
include_examples 'RfC visibility settings'
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #my_comment_requests' do
|
describe 'GET #my_comment_requests' do
|
||||||
@ -39,6 +92,8 @@ describe RequestForCommentsController do
|
|||||||
|
|
||||||
expect_http_status(:ok)
|
expect_http_status(:ok)
|
||||||
expect_template(:index)
|
expect_template(:index)
|
||||||
|
|
||||||
|
include_examples 'RfC visibility settings'
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #rfcs_with_my_comments' do
|
describe 'GET #rfcs_with_my_comments' do
|
||||||
@ -46,6 +101,8 @@ describe RequestForCommentsController do
|
|||||||
|
|
||||||
expect_http_status(:ok)
|
expect_http_status(:ok)
|
||||||
expect_template(:index)
|
expect_template(:index)
|
||||||
|
|
||||||
|
include_examples 'RfC visibility settings'
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'GET #rfcs_for_exercise' do
|
describe 'GET #rfcs_for_exercise' do
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
FactoryBot.define do
|
FactoryBot.define do
|
||||||
factory :consumer do
|
factory :consumer do
|
||||||
name { 'openHPI' }
|
name { 'openHPI' }
|
||||||
|
rfc_visibility { 'all' }
|
||||||
singleton_consumer
|
singleton_consumer
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user