Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ ruby '3.4.5'

gem 'mysql2', '~> 0.5.7'
gem 'sqlite3', '~> 1.4' # Alternative for development
gem 'puma', '~> 5.0'
gem 'puma', '~> 6.0'
gem 'rails', '~> 8.0', '>= 8.0.1'
gem 'mini_portile2', '~> 2.8' # Helps with native gem compilation
gem 'observer' # Required for Ruby 3.4.5 compatibility with Rails 8.0
Expand Down
36 changes: 33 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ GEM
term-ansicolor
thor
crass (1.0.6)
csv (3.3.5)
danger (9.5.3)
base64 (~> 0.2)
claide (~> 1.0)
Expand All @@ -128,6 +129,7 @@ GEM
debug (1.11.0)
irb (~> 1.10)
reline (>= 0.3.8)
delegate (0.4.0)
diff-lcs (1.6.2)
docile (1.4.1)
domain_name (0.6.20240107)
Expand All @@ -149,8 +151,11 @@ GEM
faraday (>= 0.8)
faraday-net_http (3.4.1)
net-http (>= 0.5.0)
faraday-retry (2.3.2)
faraday (~> 2.0)
find_with_order (1.3.1)
activerecord (>= 3)
forwardable (1.3.3)
git (2.3.3)
activesupport (>= 5.0)
addressable (~> 2.8)
Expand Down Expand Up @@ -197,10 +202,13 @@ GEM
mime-types-data (~> 3.2025, >= 3.2025.0507)
mime-types-data (3.2025.0924)
mini_mime (1.1.5)
mini_portile2 (2.8.9)
minitest (5.25.5)
mize (0.6.1)
monitor (0.2.0)
msgpack (1.8.0)
multi_json (1.17.0)
mutex_m (0.3.0)
mysql2 (0.5.7)
bigdecimal
nap (1.1.0)
Expand All @@ -217,7 +225,7 @@ GEM
net-protocol
netrc (0.11.0)
nio4r (2.5.9)
nokogiri (1.15.2-aarch64-linux)
nokogiri (1.18.10-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.10-arm64-darwin)
racc (~> 1.4)
Expand All @@ -230,6 +238,7 @@ GEM
faraday (>= 1, < 3)
sawyer (~> 0.9)
open4 (1.3.4)
ostruct (0.6.3)
parallel (1.27.0)
parser (3.3.9.0)
ast (~> 2.4.1)
Expand Down Expand Up @@ -350,6 +359,7 @@ GEM
addressable (>= 2.3.5)
faraday (>= 0.17.3, < 3)
securerandom (0.4.1)
set (1.1.2)
shoulda-matchers (6.5.0)
activesupport (>= 5.2.0)
simplecov (0.22.0)
Expand All @@ -358,7 +368,10 @@ GEM
simplecov_json_formatter (~> 0.1)
simplecov-html (0.13.2)
simplecov_json_formatter (0.1.4)
singleton (0.3.0)
spring (4.4.0)
sqlite3 (1.7.3)
mini_portile2 (~> 2.8.0)
stringio (3.1.7)
sync (0.5.0)
term-ansicolor (1.11.3)
Expand Down Expand Up @@ -398,18 +411,30 @@ PLATFORMS
DEPENDENCIES
active_model_serializers (~> 0.10.0)
bcrypt (~> 3.1.7)
bigdecimal
bootsnap (>= 1.18.4)
coveralls
csv
danger
database_cleaner-active_record
date
debug
delegate
factory_bot_rails
faker
faraday-retry
find_with_order
forwardable
jwt (~> 2.7, >= 2.7.1)
lingua
mysql2 (~> 0.5.5)
logger
mini_portile2 (~> 2.8)
monitor
mutex_m
mysql2 (~> 0.5.7)
observer
ostruct
psych (~> 5.2)
puma (~> 6.0)
rack-cors
rails (~> 8.0, >= 8.0.1)
Expand All @@ -418,14 +443,19 @@ DEPENDENCIES
rswag-specs
rswag-ui
rubocop
set
shoulda-matchers
simplecov
simplecov_json_formatter
singleton
spring
sqlite3 (~> 1.4)
timeout
tzinfo-data
uri

RUBY VERSION
ruby 3.2.7p253
ruby 3.4.5p51

BUNDLED WITH
2.4.14
36 changes: 36 additions & 0 deletions app/controllers/assignments_duties_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
class AssignmentsDutiesController < ApplicationController
include AuthorizationHelper

#before_action :authenticate_user!
before_action :action_allowed!, only: [:create, :destroy]

# GET /assignments/:assignment_id/duties
def index
assignment = Assignment.find(params[:assignment_id])
render json: assignment.duties
end

# POST /assignments/:assignment_id/duties
def create
assignment = Assignment.find(params[:assignment_id])
duty = Duty.find(params[:duty_id])
assignment.duties << duty unless assignment.duties.include?(duty)
render json: assignment.duties
end

# DELETE /assignments/:assignment_id/duties/:id
def destroy
assignment = Assignment.find(params[:assignment_id])
duty = Duty.find(params[:id])
assignment.duties.delete(duty)
head :no_content
end

private

def action_allowed!
unless current_user_has_instructor_privileges?
render json: { error: 'Not authorized' }, status: :forbidden
end
end
end
160 changes: 160 additions & 0 deletions app/controllers/concerns/authorization_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
module AuthorizationHelper
# PUBLIC METHODS

def current_user_has_super_admin_privileges?
current_user_has_privileges_of?('Super Administrator')
end

def current_user_has_admin_privileges?
current_user_has_privileges_of?('Administrator')
end

def current_user_has_instructor_privileges?
current_user_has_privileges_of?('Instructor')
end

def current_user_has_ta_privileges?
current_user_has_privileges_of?('Teaching Assistant')
end

def current_user_has_student_privileges?
current_user_has_privileges_of?('Student')
end

def current_user_is_assignment_participant?(assignment_id)
current_user.present? && AssignmentParticipant.exists?(parent_id: assignment_id, user_id: current_user.id)
end

def current_user_teaching_staff_of_assignment?(assignment_id)
assignment = Assignment.find_by(id: assignment_id)
current_user.present? &&
(current_user_instructs_assignment?(assignment) || current_user_has_ta_mapping_for_assignment?(assignment))
end

def current_user_is_a?(role_name)
current_user.present? && current_user.role&.name == role_name
end

def current_user_has_id?(id)
current_user.present? && current_user.id == id.to_i
end

def current_user_created_bookmark_id?(bookmark_id)
current_user.present? && !bookmark_id.nil? && Bookmark.find_by(id: bookmark_id.to_i)&.user_id == current_user.id
end

def given_user_can_submit?(user_id)
given_user_can?(user_id, 'submit')
end

def given_user_can_review?(user_id)
given_user_can?(user_id, 'review')
end

def given_user_can_take_quiz?(user_id)
given_user_can?(user_id, 'take_quiz')
end

def given_user_can_read?(user_id)
given_user_can_take_quiz?(user_id)
end

def response_edit_allowed?(map, user_id)
assignment = map.reviewer.assignment
if map.is_a?(ReviewResponseMap)
reviewee_team = AssignmentTeam.find(map.reviewee_id)
return current_user.present? &&
(
current_user_has_id?(user_id) ||
reviewee_team.user?(current_user) ||
current_user_has_admin_privileges? ||
(current_user_is_a?('Instructor') && current_user_instructs_assignment?(assignment)) ||
(current_user_is_a?('Teaching Assistant') && current_user_has_ta_mapping_for_assignment?(assignment))
)
end
current_user_has_id?(user_id) ||
(current_user_is_a?('Instructor') && current_user_instructs_assignment?(assignment)) ||
(assignment.course && current_user_is_a?('Teaching Assistant') && current_user_has_ta_mapping_for_assignment?(assignment))
end

def user_logged_in?
current_user.present?
end

def current_user_ancestor_of?(user)
current_user.present? && user && current_user.recursively_parent_of(user)
end

def current_user_instructs_assignment?(assignment)
current_user.present? && assignment &&
(assignment.instructor_id == current_user.id ||
(assignment.course_id && Course.find_by(id: assignment.course_id)&.instructor_id == current_user.id))
end

def current_user_has_ta_mapping_for_assignment?(assignment)
current_user.present? && assignment && assignment.course &&
TaMapping.exists?(ta_id: current_user.id, course_id: assignment.course.id)
end

def find_assignment_from_response_id(response_id)
response = Response.find_by(id: response_id.to_i)
return nil unless response
response_map = response.response_map
if response_map.assignment
response_map.assignment
else
find_assignment_from_response_id(response_map.reviewed_object_id)
end
end

def find_assignment_instructor(assignment)
if assignment.course
Course.find_by(id: assignment.course.id)&.instructor
else
assignment.instructor
end
end

def current_user_instructs_or_tas_course?(course_id)
return false unless current_user.present? && course_id.present?

course = Course.find_by(id: course_id)
return false unless course

# Check if the current user is the instructor or a TA for the course
course.instructor_id == current_user.id || TaMapping.exists?(ta_id: current_user.id, course_id: course.id)
end

# def current_user_instructs_or_tas_duty?(duty)
# return false unless current_user.present? && duty.present?

# # Check if the current user is the instructor of the duty
# return true if duty.instructor_id == current_user.id

# # Check if the current user is a TA for the course associated with the duty
# course_id = Course.find_by(instructor_id: duty.instructor_id)&.id
# current_user_instructs_or_tas_course?(course_id)
# end

# PRIVATE METHODS
private

def current_user_has_privileges_of?(role_name)
current_user.present? && current_user.role&.all_privileges_of?(Role.find_by(name: role_name))
end

def given_user_can?(user_id, action)
participant = Participant.find_by(id: user_id)
return false if participant.nil?
case action
when 'submit'
participant.can_submit
when 'review'
participant.can_review
when 'take_quiz'
participant.can_take_quiz
else
raise "Did not recognize user action '#{action}'"
end
end
end
Loading
Loading