From 3f2bb12f909952f65d94eac80dffede14e96f528 Mon Sep 17 00:00:00 2001 From: Kyle Sferrazza Date: Sun, 7 May 2023 11:02:42 -0400 Subject: [PATCH 1/7] upgrade devise --- Gemfile | 2 +- Gemfile.lock | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Gemfile b/Gemfile index 96697d841..41b81b7a1 100644 --- a/Gemfile +++ b/Gemfile @@ -19,7 +19,7 @@ gem 'webpacker', '~> 6.0.0.rc.5' gem 'react-rails' -gem 'devise', '~> 4.8.0' +gem 'devise', '~> 4.9.0' gem 'omniauth-bottlenose', git: 'https://github.com/CodeGrade/omniauth-bottlenose' gem 'omniauth-oauth2', '~> 1.7.0' gem 'omniauth-rails_csrf_protection' diff --git a/Gemfile.lock b/Gemfile.lock index c20d5bb69..144efc3b0 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -114,7 +114,7 @@ GEM database_cleaner-core (2.0.1) date (3.3.3) debug_inspector (1.1.0) - devise (4.8.1) + devise (4.9.2) bcrypt (~> 3.0) orm_adapter (~> 0.1) railties (>= 4.1.0) @@ -363,7 +363,7 @@ DEPENDENCIES byebug capybara database_cleaner - devise (~> 4.8.0) + devise (~> 4.9.0) factory_bot_rails graphql (~> 1.12.17) graphql-batch From eace27c5c531553942f007750f6c1bb1e3f3ef80 Mon Sep 17 00:00:00 2001 From: Kyle Sferrazza Date: Sun, 7 May 2023 11:07:34 -0400 Subject: [PATCH 2/7] add prints to db:populate --- lib/tasks/sample_data.rake | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/tasks/sample_data.rake b/lib/tasks/sample_data.rake index 34f914eff..2b8eb7bbb 100644 --- a/lib/tasks/sample_data.rake +++ b/lib/tasks/sample_data.rake @@ -15,6 +15,7 @@ end NUM_SIM_USERS = 1000 def create_simulation_users(lecture:, lab:, room:, exam_version:) + puts "Creating simulation users..." (0..NUM_SIM_USERS).each do |i| student = create(:user, username: "stresstest#{i}") create(:student_registration, user: student, section: lecture) @@ -26,6 +27,7 @@ end def make_sample_data ActiveRecord::Base.transaction do + puts "Loading sample data" create(:admin, username: 'admin') make_cs2500 make_cs3500 @@ -33,6 +35,7 @@ def make_sample_data end def make_cs2500 + puts "Creating CS2500..." fall2021 = create(:term, year: 2021, semester: Term.semesters['fall']) cs2500 = create(:course, title: 'CS 2500', term: fall2021) cs2500lec = create(:section, :lecture, course: cs2500) @@ -82,6 +85,7 @@ def make_cs2500 end def make_cs3500 + puts "Creating CS3500..." spring2022 = create(:term, year: 2022, semester: Term.semesters['spring']) cs3500 = create(:course, title: 'CS 3500', term: spring2022) cs3500lec = create(:section, :lecture, course: cs3500) From 5ff43071cbafac4197b49d8741a4e79be1455167 Mon Sep 17 00:00:00 2001 From: Kyle Sferrazza Date: Sun, 7 May 2023 11:22:05 -0400 Subject: [PATCH 3/7] upgrade omniauth-oauth2 --- Gemfile | 4 ++-- Gemfile.lock | 13 +++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Gemfile b/Gemfile index 41b81b7a1..8faca9f2f 100644 --- a/Gemfile +++ b/Gemfile @@ -20,8 +20,8 @@ gem 'webpacker', '~> 6.0.0.rc.5' gem 'react-rails' gem 'devise', '~> 4.9.0' -gem 'omniauth-bottlenose', git: 'https://github.com/CodeGrade/omniauth-bottlenose' -gem 'omniauth-oauth2', '~> 1.7.0' +gem 'omniauth-bottlenose', git: 'https://github.com/CodeGrade/omniauth-bottlenose', ref: '6202663' +gem 'omniauth-oauth2', '~> 1.8.0' gem 'omniauth-rails_csrf_protection' gem 'bootstrap_form', '>= 4.2.0' diff --git a/Gemfile.lock b/Gemfile.lock index 144efc3b0..98aae0cbf 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,10 @@ GIT remote: https://github.com/CodeGrade/omniauth-bottlenose - revision: 3e7c696c9b1da6823f5936a5bc7b061f3630720a + revision: 62026630bf96874214a3b20b2c859bad378a6d77 + ref: 6202663 specs: - omniauth-bottlenose (0.1.0) - omniauth-oauth2 (~> 1.7.0) + omniauth-bottlenose (0.2.0) + omniauth-oauth2 (~> 1.8.0) GEM remote: https://rubygems.org/ @@ -203,9 +204,9 @@ GEM hashie (>= 3.4.6) rack (>= 2.2.3) rack-protection - omniauth-oauth2 (1.7.3) + omniauth-oauth2 (1.8.0) oauth2 (>= 1.4, < 3) - omniauth (>= 1.9, < 3) + omniauth (~> 2.0) omniauth-rails_csrf_protection (1.0.1) actionpack (>= 4.2) omniauth (~> 2.0) @@ -373,7 +374,7 @@ DEPENDENCIES listen (~> 3.3) minitest-reporters omniauth-bottlenose! - omniauth-oauth2 (~> 1.7.0) + omniauth-oauth2 (~> 1.8.0) omniauth-rails_csrf_protection passenger (>= 5.3.2) pg (>= 0.18, < 2.0) From 8048b97b9a54bab131b13d182a0321f6b9dc658c Mon Sep 17 00:00:00 2001 From: Kyle Sferrazza Date: Sun, 7 May 2023 11:24:07 -0400 Subject: [PATCH 4/7] upgrade json-schema --- Gemfile | 2 +- Gemfile.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Gemfile b/Gemfile index 8faca9f2f..376f92373 100644 --- a/Gemfile +++ b/Gemfile @@ -44,7 +44,7 @@ gem 'pretender' gem 'bootsnap', '>= 1.4.2', require: false gem 'activerecord_json_validator', '~> 2.0' -gem 'json-schema', '~> 2.8' +gem 'json-schema', '~> 4.0' gem 'listen', '~> 3.3' diff --git a/Gemfile.lock b/Gemfile.lock index 98aae0cbf..9df150b68 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -150,8 +150,8 @@ GEM concurrent-ruby (~> 1.0) interception (0.5) json (2.6.3) - json-schema (2.8.1) - addressable (>= 2.4) + json-schema (4.0.0) + addressable (>= 2.8) json_schemer (0.2.24) ecma-re-validator (~> 0.3) hana (~> 1.3) @@ -370,7 +370,7 @@ DEPENDENCIES graphql-batch graphql-guard headless - json-schema (~> 2.8) + json-schema (~> 4.0) listen (~> 3.3) minitest-reporters omniauth-bottlenose! From fe656ff6da9431f94815f6278a3f07dd470bacc3 Mon Sep 17 00:00:00 2001 From: Kyle Sferrazza Date: Sun, 7 May 2023 13:27:43 -0400 Subject: [PATCH 5/7] upgrade graphql to latest 1.0, remove graphql-guard --- Gemfile | 3 +-- Gemfile.lock | 7 ++---- app/graphql/hourglass_schema.rb | 20 ++++++++-------- app/graphql/types/base_field.rb | 13 +++++++++++ app/graphql/types/base_object.rb | 23 +++++++++++++++++++ app/graphql/types/grading_comment_type.rb | 1 - app/graphql/types/grading_lock_type.rb | 28 +++++++++++++++-------- app/models/grading_lock.rb | 1 + 8 files changed, 68 insertions(+), 28 deletions(-) diff --git a/Gemfile b/Gemfile index 376f92373..df9c2a3ad 100644 --- a/Gemfile +++ b/Gemfile @@ -10,9 +10,8 @@ gem 'rails', '~> 6.1' # Use postgresql as the database for Active Record gem 'pg', '>= 0.18', '< 2.0' -gem 'graphql', '~> 1.12.17' +gem 'graphql', '~> 1.0' gem 'graphql-batch' -gem 'graphql-guard' # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker gem 'webpacker', '~> 6.0.0.rc.5' diff --git a/Gemfile.lock b/Gemfile.lock index 9df150b68..26f0109fd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -137,12 +137,10 @@ GEM ffi (1.15.5) globalid (1.1.0) activesupport (>= 5.0) - graphql (1.12.24) + graphql (1.13.19) graphql-batch (0.5.2) graphql (>= 1.10, < 3) promise.rb (~> 0.7.2) - graphql-guard (2.0.0) - graphql (>= 1.10.0, < 2) hana (1.3.7) hashie (5.0.0) headless (2.3.1) @@ -366,9 +364,8 @@ DEPENDENCIES database_cleaner devise (~> 4.9.0) factory_bot_rails - graphql (~> 1.12.17) + graphql (~> 1.0) graphql-batch - graphql-guard headless json-schema (~> 4.0) listen (~> 3.3) diff --git a/app/graphql/hourglass_schema.rb b/app/graphql/hourglass_schema.rb index 0f26d4c08..7f611fc6f 100644 --- a/app/graphql/hourglass_schema.rb +++ b/app/graphql/hourglass_schema.rb @@ -15,11 +15,11 @@ class HourglassSchema < GraphQL::Schema # Feedback and error messages in development mode will be # more informative than in production/test modes. if Rails.env.development? - use GraphQL::Guard.new( - not_authorized: lambda do |type, field| - GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") - end, - ) + # use GraphQL::Guard.new( + # not_authorized: lambda do |type, field| + # GraphQL::ExecutionError.new("Not authorized to access #{type}.#{field}") + # end, + # ) def self.unauthorized_object(error) # Add a top-level error to the response instead of returning nil: @@ -38,11 +38,11 @@ def self.execute(query_str = nil, **kwargs) super(query_str, **kwargs) end else - use GraphQL::Guard.new( - not_authorized: lambda do |type, field| - GraphQL::ExecutionError.new("You do not have permission to view that data.") - end, - ) + # use GraphQL::Guard.new( + # not_authorized: lambda do |type, field| + # GraphQL::ExecutionError.new("You do not have permission to view that data.") + # end, + # ) def self.unauthorized_object(error) # Add a top-level error to the response instead of returning nil: diff --git a/app/graphql/types/base_field.rb b/app/graphql/types/base_field.rb index 611eb0560..ba3667f01 100644 --- a/app/graphql/types/base_field.rb +++ b/app/graphql/types/base_field.rb @@ -3,5 +3,18 @@ module Types class BaseField < GraphQL::Schema::Field argument_class Types::BaseArgument + def guard(proc) + @guard_proc = proc + end + + def authorized?(obj, args, ctx) + return true unless @guard_proc + + wrapped = Types::GuardWrapper.new(self, obj) + answer = @guard_proc.call(wrapped, args, ctx) + return true if answer + + raise GraphQL::ExecutionError, "Not authorized to access #{self.owner_type.graphql_name}.#{self.graphql_name}." + end end end diff --git a/app/graphql/types/base_object.rb b/app/graphql/types/base_object.rb index a69458deb..426f962d9 100644 --- a/app/graphql/types/base_object.rb +++ b/app/graphql/types/base_object.rb @@ -1,10 +1,33 @@ # frozen_string_literal: true module Types + class GuardWrapper + attr_accessor :class, :object + + def initialize(cls, object) + @class = cls + @object = object + end + end + # The base class of Hourglass objects returned by GraphQL class BaseObject < GraphQL::Schema::Object field_class Types::BaseField + def self.guard(proc) + @guard_proc = proc + end + + def self.authorized?(obj, ctx) + return true unless @guard_proc + + wrapped = Types::GuardWrapper.new(self, obj) + answer = @guard_proc.call(wrapped, nil, ctx) + return true if answer + + raise GraphQL::ExecutionError, "You do not have permission to view that information." + end + module Guards def self.exam_role(user, ctx) ctx[:access_cache]&.dig(:role_for_exam, user.id) || Exam.roles[:no_reg] diff --git a/app/graphql/types/grading_comment_type.rb b/app/graphql/types/grading_comment_type.rb index fae614cae..5d90c23f5 100644 --- a/app/graphql/types/grading_comment_type.rb +++ b/app/graphql/types/grading_comment_type.rb @@ -26,6 +26,5 @@ def bnum field :message, String, null: false field :points, Float, null: false field :creator, Types::UserType, null: false - field :preset_comment, Types::PresetCommentType, null: true end end diff --git a/app/graphql/types/grading_lock_type.rb b/app/graphql/types/grading_lock_type.rb index 7435f66e1..c53c3904c 100644 --- a/app/graphql/types/grading_lock_type.rb +++ b/app/graphql/types/grading_lock_type.rb @@ -3,24 +3,31 @@ module Types class GradingLockType < Types::BaseObject implements GraphQL::Types::Relay::Node - global_id_field :id - field :id, ID, null: false, guard: ->(_obj, _args, _ctx) { true } + global_id_field :id - guard Guards::VISIBILITY + guard Guards::ALL_STAFF - field :registration, Types::RegistrationType, null: false + field :registration, Types::RegistrationType, null: false do + guard Guards::VISIBILITY + end def registration RecordLoader.for(Registration).load(object.registration_id) end - field :grader, Types::UserType, null: true, guard: ->(obj, _args, ctx) { - obj.object.grader_id.nil? || obj.object.visible_to?(ctx[:current_user], Guards.exam_role(ctx[:current_user], ctx), Guards.course_role(ctx[:current_user], ctx)) - } + + field :grader, Types::UserType, null: true do + guard ->(obj, _args, ctx) { + obj.object.grader_id.nil? || obj.object.visible_to?(ctx[:current_user], Guards.exam_role(ctx[:current_user], ctx), Guards.course_role(ctx[:current_user], ctx)) + } + end def grader RecordLoader.for(User).load(object.grader_id) end - field :completed_by, Types::UserType, null: true, guard: ->(obj, _args, ctx) { - obj.object.completed_by_id.nil? || obj.object.visible_to?(ctx[:current_user], Guards.exam_role(ctx[:current_user], ctx), Guards.course_role(ctx[:current_user], ctx)) - } + + field :completed_by, Types::UserType, null: true do + guard ->(obj, _args, ctx) { + obj.object.completed_by_id.nil? || obj.object.visible_to?(ctx[:current_user], Guards.exam_role(ctx[:current_user], ctx), Guards.course_role(ctx[:current_user], ctx)) + } + end def completed_by RecordLoader.for(User).load(object.completed_by_id) end @@ -29,6 +36,7 @@ def completed_by def qnum RecordLoader.for(Question).load(object.question_id).then{|q| q.index} end + field :pnum, Integer, null: false def pnum RecordLoader.for(Part).load(object.part_id).then{|q| q.index} diff --git a/app/models/grading_lock.rb b/app/models/grading_lock.rb index 6dd9261eb..494fb2faf 100644 --- a/app/models/grading_lock.rb +++ b/app/models/grading_lock.rb @@ -25,6 +25,7 @@ def valid_qp delegate :exam_version, to: :registration delegate :exam, to: :registration + delegate :course, to: :exam scope :incomplete, -> { where(completed_by: nil) } scope :complete, -> { where.not(completed_by: nil) } From 0efd997783f89619814252c9c4755257f2b6822d Mon Sep 17 00:00:00 2001 From: Kyle Sferrazza Date: Sun, 7 May 2023 13:31:34 -0400 Subject: [PATCH 6/7] fix deprecated graphql relay fields --- app/graphql/types/query_type.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/graphql/types/query_type.rb b/app/graphql/types/query_type.rb index 721cc3a20..d5db0f13b 100644 --- a/app/graphql/types/query_type.rb +++ b/app/graphql/types/query_type.rb @@ -4,8 +4,8 @@ module Types class QueryType < Types::BaseObject # Add root-level fields here. # They will be entry points for queries on your schema. - add_field(GraphQL::Types::Relay::NodeField) - add_field(GraphQL::Types::Relay::NodesField) + include GraphQL::Types::Relay::HasNodeField + include GraphQL::Types::Relay::HasNodesField field :impersonating, Boolean, null: false def impersonating From 50cf85ed619754c796a73ee58039d37a321a5870 Mon Sep 17 00:00:00 2001 From: Kyle Sferrazza Date: Sun, 7 May 2023 14:01:26 -0400 Subject: [PATCH 7/7] upgrade graphql to 2.0 --- Gemfile | 2 +- Gemfile.lock | 4 ++-- app/graphql/types/exam_version_type.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Gemfile b/Gemfile index df9c2a3ad..8880a7e90 100644 --- a/Gemfile +++ b/Gemfile @@ -10,7 +10,7 @@ gem 'rails', '~> 6.1' # Use postgresql as the database for Active Record gem 'pg', '>= 0.18', '< 2.0' -gem 'graphql', '~> 1.0' +gem 'graphql', '~> 2.0.0' gem 'graphql-batch' # Transpile app-like JavaScript. Read more: https://github.com/rails/webpacker diff --git a/Gemfile.lock b/Gemfile.lock index 26f0109fd..dd5341e1c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -137,7 +137,7 @@ GEM ffi (1.15.5) globalid (1.1.0) activesupport (>= 5.0) - graphql (1.13.19) + graphql (2.0.21) graphql-batch (0.5.2) graphql (>= 1.10, < 3) promise.rb (~> 0.7.2) @@ -364,7 +364,7 @@ DEPENDENCIES database_cleaner devise (~> 4.9.0) factory_bot_rails - graphql (~> 1.0) + graphql (~> 2.0.0) graphql-batch headless json-schema (~> 4.0) diff --git a/app/graphql/types/exam_version_type.rb b/app/graphql/types/exam_version_type.rb index 6847c67c9..44a599baf 100644 --- a/app/graphql/types/exam_version_type.rb +++ b/app/graphql/types/exam_version_type.rb @@ -17,7 +17,7 @@ class ExamVersionType < Types::BaseObject [ q, q.parts.map do |p| [p, p.body_items.to_a] end ] end.flatten all_qpbs.each do |qpb| - resolved = HourglassSchema.resolve_type(qpb.class, qpb, ctx) + resolved, _ = HourglassSchema.resolve_type(qpb.class, qpb, ctx) Guards.cache( ctx[:access_cache], [resolved.name, qpb.id, :visible, ctx[:current_user].id], @@ -25,7 +25,7 @@ class ExamVersionType < Types::BaseObject ) end [obj.object.rubrics, obj.object.db_references, obj.object.rubric_presets, obj.object.preset_comments].flatten.each do |r| - resolved = HourglassSchema.resolve_type(r.class, r, ctx) + resolved, _ = HourglassSchema.resolve_type(r.class, r, ctx) Guards.cache( ctx[:access_cache], [resolved.name, r.id, :visible, ctx[:current_user].id],