CSC/ECE 517 Fall 2023 - E2377. Reimplement impersonating users (functionality within impersonate controller.rb)

From Expertiza_Wiki
Jump to navigation Jump to search

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



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: -- this method will not be needed since we will not be using sessions

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 there will not be a session anymore.

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 remove anything relating to a session in this method

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 remove the session 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 remove the session from here.

Test Plan


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