diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb new file mode 100644 index 0000000..32d4bd4 --- /dev/null +++ b/app/controllers/admin_controller.rb @@ -0,0 +1,5 @@ +# frozen_string_literal: true + +class AdminController < ApplicationController + include Authentication +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5f38f02..c353756 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,4 @@ class ApplicationController < ActionController::Base - include Authentication # Only allow modern browsers supporting webp images, web push, badges, import maps, CSS nesting, and CSS :has. allow_browser versions: :modern diff --git a/app/controllers/classroom_rosters_controller.rb b/app/controllers/classroom_rosters_controller.rb index 9c0f608..f118800 100644 --- a/app/controllers/classroom_rosters_controller.rb +++ b/app/controllers/classroom_rosters_controller.rb @@ -1,6 +1,6 @@ class ClassroomRostersController < ApplicationController + include StudentAuthentication allow_unauthenticated_access - def show @classroom = Classroom.includes(:students).find_by!(uuid: params.expect(:uuid)) end diff --git a/app/controllers/classrooms_controller.rb b/app/controllers/classrooms_controller.rb index bf715f6..2077184 100644 --- a/app/controllers/classrooms_controller.rb +++ b/app/controllers/classrooms_controller.rb @@ -1,4 +1,4 @@ -class ClassroomsController < ApplicationController +class ClassroomsController < AdminController before_action :set_classroom, only: %i[ edit update ] # GET /classrooms/1/edit diff --git a/app/controllers/concerns/student_authentication.rb b/app/controllers/concerns/student_authentication.rb new file mode 100644 index 0000000..4d92195 --- /dev/null +++ b/app/controllers/concerns/student_authentication.rb @@ -0,0 +1,52 @@ +module StudentAuthentication + extend ActiveSupport::Concern + + included do + before_action :require_student_authentication + helper_method :student_authenticated? + end + + class_methods do + def allow_unauthenticated_access(**options) + skip_before_action :require_student_authentication, **options + end + end + + private + def student_authenticated? + resume_student_session + end + + def require_student_authentication + resume_student_session || request_student_authentication + end + + def resume_student_session + Current.student_session ||= find_student_session_by_cookie + end + + def find_student_session_by_cookie + StudentSession.find_by(id: cookies.signed[:student_session_id]) if cookies.signed[:student_session_id] + end + + def request_student_authentication + session[:return_to_after_authenticating] = request.url + redirect_to new_student_session_path + end + + def after_student_authentication_url + session.delete(:return_to_after_authenticating) || student_homes_url + end + + def start_new_student_session_for(student) + student.sessions.create!.tap do |session| + Current.student_session = session + cookies.signed.permanent[:student_session_id] = { value: session.id, httponly: true, same_site: :lax } + end + end + + def terminate_student_session + Current.student_session.destroy + cookies.delete(:student_session_id) + end +end diff --git a/app/controllers/passwords_controller.rb b/app/controllers/passwords_controller.rb index f95ec78..eebcc38 100644 --- a/app/controllers/passwords_controller.rb +++ b/app/controllers/passwords_controller.rb @@ -1,4 +1,4 @@ -class PasswordsController < ApplicationController +class PasswordsController < AdminController allow_unauthenticated_access before_action :set_user_by_token, only: %i[ edit update ] rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_password_path, alert: "Try again later." } diff --git a/app/controllers/schools_controller.rb b/app/controllers/schools_controller.rb index e542b75..bc0f91b 100644 --- a/app/controllers/schools_controller.rb +++ b/app/controllers/schools_controller.rb @@ -1,4 +1,4 @@ -class SchoolsController < ApplicationController +class SchoolsController < AdminController before_action :set_school, only: %i[ show edit update destroy ] # GET /schools or /schools.json diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index cf7fccd..c663cc0 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,4 +1,4 @@ -class SessionsController < ApplicationController +class SessionsController < AdminController allow_unauthenticated_access only: %i[ new create ] rate_limit to: 10, within: 3.minutes, only: :create, with: -> { redirect_to new_session_path, alert: "Try again later." } diff --git a/app/controllers/student_homes_controller.rb b/app/controllers/student_homes_controller.rb new file mode 100644 index 0000000..36a2740 --- /dev/null +++ b/app/controllers/student_homes_controller.rb @@ -0,0 +1,7 @@ +class StudentHomesController < ApplicationController + include StudentAuthentication + + def index + @student = Current.student + end +end diff --git a/app/controllers/student_sessions_controller.rb b/app/controllers/student_sessions_controller.rb new file mode 100644 index 0000000..d91284f --- /dev/null +++ b/app/controllers/student_sessions_controller.rb @@ -0,0 +1,30 @@ +class StudentSessionsController < ApplicationController + include StudentAuthentication + allow_unauthenticated_access only: %i[ create new ] + before_action :set_student, only: %i[ create ] + + def new + end + + def create + if @student + start_new_student_session_for(@student) + redirect_to after_student_authentication_url + else + redirect_to new_student_session_path, alert: "Invalid classroom or student ID." + end + end + + def destroy + terminate_student_session + redirect_to new_student_session_path, status: :see_other + end + + private + + def set_student + classroom = Classroom.find_by(uuid: params.expect(:classroom_uuid)) + return unless classroom + @student = classroom.students.find_by(id: params.expect(:student_id)) + end +end diff --git a/app/controllers/students_controller.rb b/app/controllers/students_controller.rb index 0e8e46e..0823a82 100644 --- a/app/controllers/students_controller.rb +++ b/app/controllers/students_controller.rb @@ -1,4 +1,4 @@ -class StudentsController < ApplicationController +class StudentsController < AdminController before_action :set_school, only: %i[ index new create ] before_action :set_student, only: %i[ show edit update destroy ] diff --git a/app/models/current.rb b/app/models/current.rb index 2bef56d..e591ba6 100644 --- a/app/models/current.rb +++ b/app/models/current.rb @@ -1,4 +1,6 @@ class Current < ActiveSupport::CurrentAttributes - attribute :session + attribute :session, :student_session delegate :user, to: :session, allow_nil: true + attribute :student_session + delegate :student, to: :student_session, allow_nil: true end diff --git a/app/models/student.rb b/app/models/student.rb index 6fd9a19..923373d 100644 --- a/app/models/student.rb +++ b/app/models/student.rb @@ -1,6 +1,7 @@ class Student < ApplicationRecord belongs_to :school belongs_to :classroom + has_many :sessions, class_name: "StudentSession", dependent: :destroy enum :gender, %i[male female other].index_by(&:itself) diff --git a/app/models/student_session.rb b/app/models/student_session.rb new file mode 100644 index 0000000..3ac91a8 --- /dev/null +++ b/app/models/student_session.rb @@ -0,0 +1,3 @@ +class StudentSession < ApplicationRecord + belongs_to :student +end diff --git a/app/views/classroom_rosters/show.html.erb b/app/views/classroom_rosters/show.html.erb index f500b42..d353db4 100644 --- a/app/views/classroom_rosters/show.html.erb +++ b/app/views/classroom_rosters/show.html.erb @@ -3,12 +3,14 @@
You are logged in as <%= @student.full_name %>
+If you are a student looking to log in, you need to get a special link from your teacher or facilitator.
+