CSC/ECE 517 Fall 2023 - E2377. Reimplement impersonating users (functionality within impersonate controller.rb)
Expertiza
Expertiza is a Ruby on Rails based open source project. Instructors have the ability to add new projects, assignments, etc., as well as edit existing ones. Later on, they can view student submissions and grade them. Students can also use Expertiza to organize into teams to work on different projects and assignments and submit their work. They can also review other students' submissions.
Problem Statement
The current expertiza platform allows authorized users to impersonate others, utilizing session-based authentication. The challenge is to reimplement this feature in the new expertiza backend (https://github.com/expertiza/reimplementation-back-end), which employs JWT token authentication.
Key challenges include adapting the existing session-based logic to work seamlessly with JWT tokens and transitioning from a Model-View-Controller (MVC) architecture to an API-only setup, requiring responses in JSON format. The goal is to ensure the smooth integration of the impersonation feature into the new backend while addressing authentication changes and architectural shifts.
Solution
Design
Methods Reimplemented
Original
def action_allowed? # Check for TA privileges first since TA's also have student privileges. if ['Student'].include? current_role_name !session[:super_user].nil? else ['Super-Administrator', 'Administrator', 'Instructor', 'Teaching Assistant'].include? current_role_name end end
Updated
-- we will remove the session and store the user with a JWT token to authenticate
Original
def auto_complete_for_user_name @users = session[:user].get_available_users(params[:user][:name]) render inline: "<%= auto_complete_result @users, 'name' %>", layout: false end
Updated
--instead of using session we will just access the current user -- we will remove the inline rendering and return a JSON instead
Original
def start flash[:error] = "This page doesn't take any query string." unless request.GET.empty? end
Updated --we will remove the flash error as it will not work in the new implementation
Original:
def generate_session(user) AuthController.clear_user_info(session, nil) session[:original_user] = @original_user session[:impersonate] = true session[:user] = user end
Updated:
-- We will not be using sessions so we will be using JWT tokens to authenticate users instead of making a traditional session
Original
def overwrite_session if params[:impersonate].nil? user = real_user(params[:user][:name]) session[:super_user] = session[:user] if session[:super_user].nil? generate_session(user) elsif !params[:impersonate][:name].empty? user = real_user(params[:impersonate][:name]) generate_session(user) else session[:user] = session[:super_user] session[:super_user] = nil end end
Updated --instead, we will just update the current User but we will authenticate the user with a JWT token instead.
Original
def check_if_input_is_valid if params[:user] && warn_for_special_chars(params[:user][:name], 'Username') flash[:error] = 'Please enter valid user name' redirect_back fallback_location: root_path elsif params[:impersonate] && warn_for_special_chars(params[:impersonate][:name], 'Username') flash[:error] = 'Please enter valid user name' redirect_back fallback_location: root_path end end
Updated: -- in this method we will remove the flash errors
Original
def check_if_user_impersonateable if params[:impersonate].nil? user = real_user(params[:user][:name]) unless @original_user.can_impersonate? user @message = "You cannot impersonate '#{params[:user][:name]}'." temp AuthController.clear_user_info(session, nil) else overwrite_session end else unless params[:impersonate][:name].empty? overwrite_session end end end
Updated:
--we will change the session to a JWT token in this method
Original
def impersonate begin @original_user = session[:super_user] || session[:user] if params[:impersonate].nil? @message = "You cannot impersonate '#{params[:user][:name]}'." @message = 'User name cannot be empty' if params[:user][:name].empty? user = real_user(params[:user][:name]) check_if_user_impersonateable if user elsif !params[:impersonate][:name].empty? # Impersonate a new account @message = "You cannot impersonate '#{params[:impersonate][:name]}'." user = real_user(params[:impersonate][:name]) check_if_user_impersonateable if user # Revert to original account when currently in the impersonated session elsif !session[:super_user].nil? AuthController.clear_user_info(session, nil) session[:user] = session[:super_user] user = session[:user] session[:super_user] = nil end # Navigate to user's home location as the default landing page after impersonating or reverting AuthController.set_current_role(user.role_id, session) redirect_to action: AuthHelper.get_home_action(session[:user]), controller: AuthHelper.get_home_controller(session[:user]) rescue StandardError flash[:error] = @message redirect_back fallback_location: root_path end end
Updated: -- we will change the session to a JWT token and flash message from this method.
Original:
def real_user(name) if User.anonymized_view?(session[:ip]) user = User.real_user_from_anonymized_name(name) else user = User.find_by(name: name) end return user end
Updated: -- we will implement a JWT token instead of using a session here
Test Plan
For the testing of this project, we will first test all functionality using the Postman API. If everything works as expected, we will then start testing our program using the RSwag tool. This will allow us to test and explore operations using a UI and directly from the rspec integration tests. The video links for these testing methods will be posted here when we complete it.
Team
Mentor
Renji Joseph Sabu <rsabu@ncsu.edu>
Students
Manoj Ayyappan <mayyapp@ncsu.edu>
Pradeep Patil <papatil@ncsu.edu>
Maya Patel <mdpatel2@ncsu.edu>
Pull Request
Changes for this project are under Expertiza Pull Request