Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

responds only to valid MIME types for users controller #3743

Merged
merged 2 commits into from
Apr 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading