Allow teachers to access internal users and manage them in their study groups

This commit is contained in:
Sebastian Serth
2022-09-22 17:55:10 +02:00
committed by Sebastian Serth
parent e3018c1847
commit 0e7c38657f
11 changed files with 45 additions and 22 deletions

View File

@ -11,13 +11,10 @@ class StudyGroupsController < ApplicationController
authorize!
end
def show
@search = @study_group.external_users.ransack(params[:q])
end
def show; end
def edit
@search = @study_group.external_users.ransack(params[:q])
@members = StudyGroupMembership.where(user: @search.result, study_group: @study_group)
@members = StudyGroupMembership.where(user: @study_group.users, study_group: @study_group).includes(:user)
end
def update

View File

@ -15,7 +15,7 @@ class StudyGroup < ApplicationRecord
end
def user_count
external_users.count + internal_users.count
study_group_memberships.distinct.count
end
def to_s

View File

@ -23,7 +23,14 @@ class User < ApplicationRecord
scope :with_submissions, -> { where('id IN (SELECT user_id FROM submissions)') }
scope :in_study_group_of, lambda {|user|
joins(:study_group_memberships).where(study_group_memberships: {study_group_id: user.study_groups}) unless user.admin?
unless user.admin?
joins(:study_group_memberships)
.where(study_group_memberships: {
study_group_id: user.study_group_memberships
.where(study_group_memberships: {role: StudyGroupMembership.roles[:teacher]})
.select(:study_group_id),
})
end
}
validates :platform_admin, boolean_presence: true
@ -65,7 +72,7 @@ class User < ApplicationRecord
end
def self.ransackable_attributes(auth_object)
if auth_object.admin?
if auth_object.present? && auth_object.admin?
%w[name email external_id consumer_id platform_admin]
else
%w[name external_id]

View File

@ -2,10 +2,14 @@
class InternalUserPolicy < AdminOnlyPolicy
def destroy?
super && !@record.admin?
admin? && !@record.admin?
end
def index?
admin? || teacher?
end
def show?
super || @record == @user
admin? || @record == @user || teacher_in_study_group?
end
end

View File

@ -32,12 +32,15 @@ h1 = ExternalUser.model_name.human(count: 2)
tr
th = t('activerecord.attributes.external_user.name')
th = t('activerecord.attributes.external_user.consumer')
th = t('activerecord.attributes.external_user.platform_admin')
th = t('shared.actions')
tbody
- @users.each do |user|
tr
td = link_to_if(policy(user).show?, user.displayname, user)
td = link_to_if(user.consumer.present? && policy(user.consumer).show?, user.consumer, user.consumer)
- if current_user.admin?
td = symbol_for(user.platform_admin?)
td = link_to(t('shared.show'), user) if policy(user).show?
= render('shared/pagination', collection: @users)

View File

@ -6,7 +6,7 @@ h1 = @user.displayname
code
= @user.external_id
= row(label: 'external_user.consumer', value: link_to_if(policy(@user.consumer).show?, @user.consumer, @user.consumer))
= row(label: 'external_user.platform_admin', value: @user.platform_admin?)
= row(label: 'external_user.platform_admin', value: @user.platform_admin?) if current_user.admin?
= row(label: 'users.show.study_groups') do
- visible_memberships = @user.study_group_memberships.select { |study_group_membership| policy(study_group_membership.study_group).show? }
- if visible_memberships.any?

View File

@ -1,6 +1,6 @@
h1 = InternalUser.model_name.human(count: 2)
= render(layout: 'shared/form_filters') do |f|
= render(layout: 'shared/form_filters') if current_user.admin? do |f|
.col-auto
= f.label(:consumer_id_eq, t('activerecord.attributes.internal_user.consumer'), class: 'visually-hidden form-label')
= f.collection_select(:consumer_id_eq, Consumer.with_internal_users, :id, :name, class: 'form-control', include_blank: true, prompt: t('activerecord.attributes.internal_user.consumer'))
@ -15,14 +15,16 @@ h1 = InternalUser.model_name.human(count: 2)
tr
th = t('activerecord.attributes.internal_user.name')
th = t('activerecord.attributes.internal_user.consumer')
th = t('activerecord.attributes.internal_user.platform_admin')
- if current_user.admin?
th = t('activerecord.attributes.internal_user.platform_admin')
th colspan=3 = t('shared.actions')
tbody
- @users.each do |user|
tr
td = link_to_if(policy(user).show?, user.name, user)
td = user.consumer ? link_to_if(policy(user.consumer).show?, user.consumer, user.consumer) : empty
td = symbol_for(user.platform_admin?)
- if current_user.admin?
td = symbol_for(user.platform_admin?)
td = link_to(t('shared.show'), user) if policy(user).show?
td = link_to(t('shared.edit'), edit_internal_user_path(user)) if policy(user).edit?
td = link_to(t('shared.destroy'), user, data: {confirm: t('shared.confirm_destroy')}, method: :delete) if policy(user).destroy?

View File

@ -2,10 +2,10 @@ h1
= @user
= render('shared/edit_button', object: @user)
= row(label: 'internal_user.email', value: @user.email)
= row(label: 'internal_user.email', value: @user.email) if current_user.admin?
= row(label: 'internal_user.name', value: @user.name)
= row(label: 'internal_user.consumer', value: @user.consumer ? link_to_if(policy(@user.consumer).show?, @user.consumer, @user.consumer) : nil)
= row(label: 'internal_user.platform_admin', value: @user.platform_admin?)
= row(label: 'internal_user.platform_admin', value: @user.platform_admin?) if current_user.admin?
= row(label: 'internal_user.activated', value: @user.activated?)
= row(label: 'users.show.study_groups') do
- visible_memberships = @user.study_group_memberships.select { |study_group_membership| policy(study_group_membership.study_group).show? }
@ -28,4 +28,5 @@ h1
- else
= t('users.show.no_groups')
= row(label: 'codeharbor_link.profile_label', value: @user.codeharbor_link.nil? ? link_to(t('codeharbor_link.new'), new_codeharbor_link_path, class: 'btn btn-secondary') : link_to(t('codeharbor_link.edit'), edit_codeharbor_link_path(@user.codeharbor_link), class: 'btn btn-secondary'))
- if @user == current_user
= row(label: 'codeharbor_link.profile_label', value: @user.codeharbor_link.nil? ? link_to(t('codeharbor_link.new'), new_codeharbor_link_path, class: 'btn btn-secondary') : link_to(t('codeharbor_link.edit'), edit_codeharbor_link_path(@user.codeharbor_link), class: 'btn btn-secondary'))

View File

@ -6,11 +6,11 @@
h3 = t('activerecord.attributes.study_group.members')
.table-responsive
table.table
table.sortable.table
thead
tr
th = t('activerecord.attributes.exercise.selection')
th = sort_link(@search, :name, t('navigation.sections.users'))
th = t('navigation.sections.users')
= collection_check_boxes :study_group, :study_group_membership_ids, @members, :id, :id do |b|
tr
td = b.check_box class: 'form-check-input'

View File

@ -12,10 +12,10 @@ h1
h2.mt-4 = t('activerecord.attributes.study_group.members')
.table-responsive
table.table
table.sortable.table
thead
tr
th = sort_link(@search, :name, t('navigation.sections.users'))
th = t('navigation.sections.users')
- @study_group.users.each do |user|
tr
td = link_to_if(policy(user).show?, user.displayname, user)

View File

@ -5,7 +5,7 @@ require 'rails_helper'
describe InternalUserPolicy do
subject(:policy) { described_class }
%i[create? edit? index? new? show? update?].each do |action|
%i[create? edit? new? show? update?].each do |action|
permissions(action) do
it 'grants access to admins only' do
expect(policy).to permit(build(:admin), InternalUser.new)
@ -16,6 +16,15 @@ describe InternalUserPolicy do
end
end
permissions :index? do
it 'grants access to admins and teachers only' do
expect(policy).not_to permit(build(:external_user), InternalUser.new)
%i[admin teacher].each do |factory_name|
expect(policy).to permit(create(factory_name), InternalUser.new)
end
end
end
permissions :destroy? do
context 'with an admin user' do
it 'grants access to no one' do