Skip to content

Commit

Permalink
responds only to valid MIME types for users controller (#3743)
Browse files Browse the repository at this point in the history
* responds only to valid MIME types for users controller

* adds YARD documentation
  • Loading branch information
saikumar9 authored and jacobkagon committed May 15, 2024
1 parent effe740 commit c24bb87
Show file tree
Hide file tree
Showing 5 changed files with 334 additions and 18 deletions.
30 changes: 30 additions & 0 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ def format_js?
# for current_user
before_action :set_current_user

# Handles ActionController::UnknownFormat exception by calling the +render_unsupported_format+ method.
#
# @see #render_unsupported_format
#
# @example
# rescue_from ActionController::UnknownFormat, with: :render_unsupported_format
#
rescue_from ActionController::UnknownFormat, with: :render_unsupported_format

rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

rescue_from ActionController::InvalidCrossOriginRequest do |exception|
Expand Down Expand Up @@ -107,6 +116,27 @@ def create_sso_account(user, personish, timeout, account_role = "individual")

private

# Renders an 'Unsupported format' message with a status of :not_acceptable for various formats.
# This method is used to handle requests in unsupported formats.
#
# @example
# render_unsupported_format
#
# @return [ActionController::Response] A response with a plain text body and a status of :not_acceptable.
def render_unsupported_format
respond_to do |format|
format.html { render plain: 'Unsupported format', status: :not_acceptable }
format.json { render json: { error: 'Unsupported format' }, status: :not_acceptable }
format.xml { render xml: '<error>Unsupported format</error>', status: :not_acceptable }
format.csv { render plain: 'Unsupported format', status: :not_acceptable }
format.text { render plain: 'Unsupported format', status: :not_acceptable }
format.js { render plain: 'Unsupported format', status: :not_acceptable }

# Default handler for any other format
format.any { render plain: 'Unsupported format', status: :not_acceptable }
end
end

def redirect_if_prod
redirect_to root_path, :flash => { :error => "Unable to run seeds on prod environment." } unless ENV['ENROLL_REVIEW_ENVIRONMENT'] == 'true' || !Rails.env.production?
end
Expand Down
90 changes: 81 additions & 9 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
@@ -1,40 +1,90 @@
# frozen_string_literal: true

# UsersController handles actions related to User in the context of HbxProfile.
#
# @see ApplicationController
class UsersController < ApplicationController

# Sets the user before each action, except for :confirm_lock and :unsupported_browser.
before_action :set_user, except: [:confirm_lock, :unsupported_browser]

# Confirms the lock action for a user.
#
# @return [void]
# @response_to JS
def confirm_lock
authorize HbxProfile, :confirm_lock?
@user_id = params[:user_action_id]

respond_to do |format|
format.js { render 'confirm_lock' }
end
end

# Locks a user.
#
# @return [void]
# @response_to JS
def lockable
authorize HbxProfile, :lockable?
@user.lock!
flash[:notice] = "User #{user.email} is successfully #{user.lockable_notice}."
render file: 'users/lockable.js.erb'

respond_to do |format|
format.js { render 'lockable' }
end
end

# Resets a user's password.
#
# @return [void]
# @response_to JS
def reset_password
authorize HbxProfile, :reset_password?
render file: 'users/reset_password.js.erb'

respond_to do |format|
format.js { render 'reset_password' }
end
end

# Confirms the reset password action for a user.
#
# @return [void]
# @response_to JS
def confirm_reset_password
authorize HbxProfile, :confirm_reset_password?
@error = nil
validate_email if params[:user].present?
if @error.nil?
User.send_reset_password_instructions(email: @user.email)
redirect_to user_account_index_exchanges_hbx_profiles_url, notice: "Reset password instruction sent to user email."
else
render file: 'users/reset_password.js.erb'

respond_to do |format|
format.js do
if @error.nil?
User.send_reset_password_instructions(email: @user.email)
redirect_to user_account_index_exchanges_hbx_profiles_url, notice: "Reset password instruction sent to user email."
else
render 'reset_password'
end
end
end
end

# Changes a user's username and email.
#
# @return [void]
# @response_to JS
def change_username_and_email
authorize HbxProfile, :change_username_and_email?
@user_id = params[:user_id]

respond_to do |format|
format.js { render 'change_username_and_email' }
end
end

# Confirms the change username and email action for a user.
#
# @return [void]
# @response_to JS
def confirm_change_username_and_email
authorize HbxProfile, :confirm_change_username_and_email?
@element_to_replace_id = params[:family_actions_id]
Expand All @@ -58,13 +108,29 @@ def confirm_change_username_and_email
end
end

# Shows a user's login history.
#
# @return [void]
# @response_to JS
def login_history
authorize HbxProfile, :login_history?
@user_login_history = SessionIdHistory.for_user(user_id: @user.id).order('created_at DESC').page(params[:page]).per(15)

respond_to do |format|
format.js { render 'login_history' }
end
end

# Auth not required
def unsupported_browser; end
# Shows the unsupported browser page.
#
# @return [void]
# @response_to HTML
# @note Authentication and Authorization are not required
def unsupported_browser
respond_to do |format|
format.html { render 'unsupported_browser' }
end
end

private

Expand All @@ -82,10 +148,16 @@ def validate_email
end
end

# Returns the user.
#
# @return [User] The user.
def user
@user ||= User.find(params[:id])
end

# Sets the user.
#
# @return [void]
def set_user
@user = User.find(params[:id])
end
Expand Down
153 changes: 153 additions & 0 deletions spec/controllers/users_controller_mime_types_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe UsersController, type: :controller do

before :all do
DatabaseCleaner.clean
end

after :all do
DatabaseCleaner.clean
end


describe 'with authorization and authentication' do
let(:person) { FactoryBot.create(:person, :with_hbx_staff_role) }
let(:hbx_staff_role) { person.hbx_staff_role }
let(:permission) { FactoryBot.create(:permission, :full_access_super_admin) }
let(:admin) { FactoryBot.create(:user, person: person) }

let(:user_person) { FactoryBot.create(:person) }
let(:user) { FactoryBot.create(:user, person: user_person) }
let(:family) { FactoryBot.create(:family, :with_primary_family_member, person: user_person) }

before do
hbx_staff_role.update_attributes!(permission_id: permission.id)
sign_in(admin)
end

describe 'GET #confirm_lock' do
shared_examples 'responds_to_mime_type for GET endpoint confirm_lock' do |unsupported_mime_types|
unsupported_mime_types.each do |mime_type|
it "rejects #{mime_type}" do
get :confirm_lock, params: { id: user.id, format: mime_type }
expect(response).to have_http_status(:not_acceptable)
end
end
end

include_examples 'responds_to_mime_type for GET endpoint confirm_lock', [:html, :json, :xml, :csv, :text]
end

describe 'GET #lockable' do
shared_examples 'responds_to_mime_type for GET endpoint lockable' do |unsupported_mime_types|
unsupported_mime_types.each do |mime_type|
it "rejects #{mime_type}" do
get :lockable, params: { id: user.id, format: mime_type }
expect(response).to have_http_status(:not_acceptable)
end
end
end

include_examples 'responds_to_mime_type for GET endpoint lockable', [:html, :json, :xml, :csv, :text]
end

describe 'GET #reset_password' do
shared_examples 'responds_to_mime_type for GET endpoint reset_password' do |unsupported_mime_types|
unsupported_mime_types.each do |mime_type|
it "rejects #{mime_type}" do
get :reset_password, params: { id: user.id, format: mime_type }
expect(response).to have_http_status(:not_acceptable)
end
end
end

include_examples 'responds_to_mime_type for GET endpoint reset_password', [:html, :json, :xml, :csv, :text]
end

describe 'PUT #confirm_reset_password' do
shared_examples 'responds_to_mime_type for PUT endpoint confirm_reset_password' do |unsupported_mime_types|
unsupported_mime_types.each do |mime_type|
it "rejects #{mime_type}" do
put :confirm_reset_password, params: { id: user.id, format: mime_type }
expect(response).to have_http_status(:not_acceptable)
end
end
end

include_examples 'responds_to_mime_type for PUT endpoint confirm_reset_password', [:html, :json, :xml, :csv, :text]
end

describe 'GET #change_username_and_email' do
shared_examples 'responds_to_mime_type for GET endpoint change_username_and_email' do |unsupported_mime_types|
unsupported_mime_types.each do |mime_type|
it "rejects #{mime_type}" do
get :change_username_and_email, params: {
id: user.id,
format: mime_type
}
expect(response).to have_http_status(:not_acceptable)
end
end
end

include_examples 'responds_to_mime_type for GET endpoint change_username_and_email', [:html, :json, :xml, :csv, :text]
end

describe 'POST #confirm_change_username_and_email' do
let(:person2) { FactoryBot.create(:person) }
let(:user2) { FactoryBot.create(:user, person: person2) }

shared_examples 'responds_to_mime_type for POST endpoint confirm_change_username_and_email' do |unsupported_mime_types|
unsupported_mime_types.each do |mime_type|
it "rejects #{mime_type}" do
post :confirm_change_username_and_email, params: {
id: user.id,
family_actions_id: family.id,
new_email: user2.email,
new_oim_id: user2.oim_id,
format: mime_type
}
expect(response).to have_http_status(:not_acceptable)
end
end
end

include_examples 'responds_to_mime_type for POST endpoint confirm_change_username_and_email', [:html, :json, :xml, :csv, :text]
end

describe 'GET #login_history' do
shared_examples 'responds_to_mime_type for GET endpoint login_history' do |unsupported_mime_types|
unsupported_mime_types.each do |mime_type|
it "rejects #{mime_type}" do
get :login_history, params: {
id: user.id,
format: mime_type
}
expect(response).to have_http_status(:not_acceptable)
end
end
end

include_examples 'responds_to_mime_type for GET endpoint login_history', [:html, :json, :xml, :csv, :text]
end
end

describe 'without authorization and authentication' do

describe 'GET #unsupported_browser' do
shared_examples 'responds_to_mime_type for GET endpoint unsupported_browser' do |unsupported_mime_types|
unsupported_mime_types.each do |mime_type|
it "rejects #{mime_type}" do
get :unsupported_browser, params: { format: mime_type }
expect(response).to have_http_status(:not_acceptable)
end
end
end

include_examples 'responds_to_mime_type for GET endpoint unsupported_browser', [:js, :json, :xml, :csv, :text]
end
end
end
Loading

0 comments on commit c24bb87

Please sign in to comment.