Skip to content
Open
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
5 changes: 5 additions & 0 deletions app/api/activity_types_public_api.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
require 'grape'

class ActivityTypesPublicApi < Grape::API
helpers AuthenticationHelpers

before do

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block is being repeated in multiple APIs, would it be better to maybe move it to a shared require_authentication! helper in AuthenticationHelpers just to keep the code DRY?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion, Samindi! I actually tested moving this into a shared require_authentication! helper, but ran into issues that the helper didn’t behave consistently across all API classes (some edge cases weren’t triggering authentication as expected).

Given that, I think keeping the before block inline here is currently the safer option. It ensures the authentication logic runs exactly as intended for this endpoint without introducing unexpected side effects. We can definitely revisit a shared helper later if we find a reliable implementation that works across all APIs.

error!({ error: '401 Unauthorized' }, 401) unless authenticated_without_error?
end
desc "Get an activity type details"
get '/activity_types/:id' do
present ActivityType.find(params[:id]), with: Entities::ActivityTypeEntity
Expand Down
2 changes: 2 additions & 0 deletions app/api/api_root.rb
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,15 @@ class ApiRoot < Grape::API
AuthenticationHelpers.add_auth_to Admin::OverseerAdminApi

AuthenticationHelpers.add_auth_to ActivityTypesAuthenticatedApi
AuthenticationHelpers.add_auth_to ActivityTypesPublicApi
AuthenticationHelpers.add_auth_to BreaksApi
AuthenticationHelpers.add_auth_to DiscussionCommentApi
AuthenticationHelpers.add_auth_to ExtensionCommentsApi
AuthenticationHelpers.add_auth_to GroupSetsApi
AuthenticationHelpers.add_auth_to LearningOutcomesApi
AuthenticationHelpers.add_auth_to LearningAlignmentApi
AuthenticationHelpers.add_auth_to ProjectsApi
AuthenticationHelpers.add_auth_to SettingsApi
AuthenticationHelpers.add_auth_to StudentsApi
AuthenticationHelpers.add_auth_to Submission::PortfolioApi
AuthenticationHelpers.add_auth_to Submission::PortfolioEvidenceApi
Expand Down
5 changes: 5 additions & 0 deletions app/api/campuses_public_api.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
require 'grape'

class CampusesPublicApi < Grape::API
helpers AuthenticationHelpers

before do
error!({ error: '401 Unauthorized' }, 401) unless authenticated_without_error?
end
desc "Get a campus details"
get '/campuses/:id' do
campus = Campus.find(params[:id])
Expand Down
5 changes: 5 additions & 0 deletions app/api/settings_api.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
require 'grape'

class SettingsApi < Grape::API
helpers AuthenticationHelpers

before do
error!({ error: '401 Unauthorized' }, 401) unless authenticated_without_error?
end
#
# Returns the current auth method
#
Expand Down
5 changes: 5 additions & 0 deletions app/api/teaching_periods_public_api.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
require 'grape'

class TeachingPeriodsPublicApi < Grape::API
helpers AuthenticationHelpers

before do
error!({ error: '401 Unauthorized' }, 401) unless authenticated_without_error?
end
desc "Get a teaching period's details"
get '/teaching_periods/:id' do
teaching_period = TeachingPeriod.find(params[:id])
Expand Down
13 changes: 13 additions & 0 deletions app/helpers/authentication_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,19 @@ def authenticated?
end
end

#
# Check authentication without raising an error.
# Returns true if authenticated, false otherwise.
#
def authenticated_without_error?
catch(:error) do
return true if authenticated?
end
false
end

module_function :authenticated_without_error?

#
# Get the current user either from warden or from the header
#
Expand Down
90 changes: 55 additions & 35 deletions test/api/activity_types_api_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@ def app
Rails.application
end

def test_get_all_activity_types_requires_authentication
get '/api/activity_types'
assert_equal 401, last_response.status
end

def test_get_activity_type_by_id_requires_authentication
activity_type = FactoryBot.create(:activity_type)
get "/api/activity_types/#{activity_type.id}"
assert_equal 401, last_response.status
end

def test_get_all_activity_types
add_auth_header_for(user: User.first)
get '/api/activity_types'
expected_data = ActivityType.all

Expand All @@ -23,12 +35,20 @@ def test_get_all_activity_types
end
end

def test_get_activity_type_by_id
activity_type = FactoryBot.create(:activity_type)
add_auth_header_for(user: User.first)
get "/api/activity_types/#{activity_type.id}"
response_keys = %w(name abbreviation)
assert_json_matches_model(activity_type, last_response_body, response_keys)
end

# POST tests
# 1: Admin can create a new activity type
def test_admin_can_post_activity_type
# Admin user
admin = FactoryBot.create(:user, :admin)

# the number of teaching period before post
no_activity_type = ActivityType.count

Expand All @@ -42,8 +62,8 @@ def test_admin_can_post_activity_type

# perform the POST
post_json '/api/activity_types', data_to_post
# check if the request get through

# check if the request get through
assert_equal 201, last_response.status

# check if the details posted match as expected
Expand All @@ -63,7 +83,7 @@ def test_admin_can_post_activity_type
def test_convenor_cannot_post_activity_type
# Convenor user
convenor = FactoryBot.create(:user, :convenor)

# the number of teaching period before post
no_activity_type = ActivityType.count

Expand All @@ -74,11 +94,11 @@ def test_convenor_cannot_post_activity_type

# auth_token and username added to header
add_auth_header_for(user: convenor)

# perform the POST
post_json '/api/activity_types', data_to_post
# check if the request get through

# check if the request get through
assert_equal 403, last_response.status

# check if no more activity type is created
Expand All @@ -89,7 +109,7 @@ def test_convenor_cannot_post_activity_type
def test_tutor_cannot_post_activity_type
# Tutor user
tutor = FactoryBot.create(:user, :tutor)

# the number of teaching period before post
no_activity_type = ActivityType.count

Expand All @@ -100,11 +120,11 @@ def test_tutor_cannot_post_activity_type

# auth_token and username added to header
add_auth_header_for(user: tutor)

# perform the POST
post_json '/api/activity_types', data_to_post
# check if the request get through

# check if the request get through
assert_equal 403, last_response.status

# check if no more activity type is created
Expand All @@ -119,8 +139,8 @@ def test_admin_can_put_activity_types

# The activity type to be replaced
activity_type = FactoryBot.create(:activity_type)
# Data to replace

# Data to replace
data_to_put = {
activity_type: FactoryBot.build(:activity_type)
}
Expand All @@ -130,7 +150,7 @@ def test_admin_can_put_activity_types

# Update activity_type with data_to_put
put_json "/api/activity_types/#{activity_type.id}", data_to_put

#check if the request get through
assert_equal 200, last_response.status

Expand All @@ -139,7 +159,7 @@ def test_admin_can_put_activity_types
activity_type_updated = activity_type.reload
assert_json_matches_model(activity_type_updated, last_response_body, response_keys)

# check if the details in the replaced teaching period match as data set to replace
# check if the details in the replaced teaching period match as data set to replace
assert_equal data_to_put[:activity_type]['name'], activity_type_updated.name
assert_equal data_to_put[:activity_type]['abbreviation'], activity_type_updated.abbreviation
end
Expand All @@ -151,8 +171,8 @@ def test_convenor_cannot_put_activity_types

# The activity type to be replaced
activity_type = FactoryBot.create(:activity_type)
# Data to replace

# Data to replace
data_to_put = {
activity_type: FactoryBot.build(:activity_type)
}
Expand All @@ -162,7 +182,7 @@ def test_convenor_cannot_put_activity_types

# Update activity_type with data_to_put
put_json "/api/activity_types/#{activity_type.id}", data_to_put

#check if the request get through
assert_equal 403, last_response.status
end
Expand All @@ -174,8 +194,8 @@ def test_tutor_cannot_put_activity_types

# The activity type to be replaced
activity_type = FactoryBot.create(:activity_type)
# Data to replace

# Data to replace
data_to_put = {
activity_type: FactoryBot.build(:activity_type)
}
Expand All @@ -185,7 +205,7 @@ def test_tutor_cannot_put_activity_types

# Update activity_type with data_to_put
put_json "/api/activity_types/#{activity_type.id}", data_to_put

#check if the request get through
assert_equal 403, last_response.status
end
Expand All @@ -209,11 +229,11 @@ def test_student_cannot_post_activity_type
post_json '/api/activity_types', data_to_post

# Check if the post does not get through
assert_equal 403, last_response.status
assert_equal 403, last_response.status

# Check if the number of activity type is the same as initially
assert_equal ActivityType.count, number_of_activity_type
end
end

def test_student_cannot_put_activity_type
# A user with student role which does not have premision to put a activity type
Expand All @@ -223,7 +243,7 @@ def test_student_cannot_put_activity_type
activity_type = FactoryBot.create(:activity_type)

# Number of Activity type before put new activity type
number_of_activity_type = ActivityType.count
number_of_activity_type = ActivityType.count

# Create a dummy activity type
data_to_put = {
Expand All @@ -235,9 +255,9 @@ def test_student_cannot_put_activity_type

# Perform PUT, but the student user does not have permissions to put it.
put_json "/api/activity_types/#{activity_type.id}", data_to_put

# Check if the put does not get through
assert_equal 403, last_response.status
assert_equal 403, last_response.status

# Check if the number of activity type is the same as initially
assert_equal ActivityType.count, number_of_activity_type
Expand All @@ -246,17 +266,17 @@ def test_student_cannot_put_activity_type
def test_delete_activity_type
# Create a activity type
activity_type = FactoryBot.create(:activity_type)

#number of activity type before delete
number_of_ativity_type = ActivityType.count


# auth_token and username added to header
add_auth_header_for(user: User.first)

# perform the delete
delete_json "/api/activity_types/#{activity_type.id}"
delete_json "/api/activity_types/#{activity_type.id}"

# Check if the delete get through
assert_equal 200, last_response.status

Expand All @@ -270,18 +290,18 @@ def test_delete_activity_type
def test_student_cannot_delete_activity_type
# A user with student role which does not have permision to delete a activity type
user = FactoryBot.build(:user, :student)

# create a activity type to delete
activity_type = FactoryBot.create (:activity_type)

# number of activity type before delete
number_of_ativity_type = ActivityType.count

# auth_token and username added to header
add_auth_header_for(user: user)

# perform the delete
delete_json "/api/activity_types/#{activity_type.id}"
delete_json "/api/activity_types/#{activity_type.id}"

# check if the delete does not get through
assert_equal 403, last_response.status
Expand Down
13 changes: 13 additions & 0 deletions test/api/campuses_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,19 @@ def app
Rails.application
end

def test_get_all_campuses_requires_authentication
get '/api/campuses'
assert_equal 401, last_response.status
end

def test_get_campus_by_id_requires_authentication
campus = FactoryBot.create(:campus, mode: 'timetable')
get "/api/campuses/#{campus.id}"
assert_equal 401, last_response.status
end

def test_get_all_campuses
add_auth_header_for(user: User.first)
get '/api/campuses'
expected_data = Campus.all
assert_equal expected_data.count, last_response_body.count
Expand All @@ -22,6 +34,7 @@ def test_get_all_campuses

def test_get_campuses_by_id
campus = FactoryBot.create(:campus, mode: 'timetable')
add_auth_header_for(user: User.first)
get "/api/campuses/#{campus.id}"
response_keys = %w(name abbreviation)
assert_json_matches_model(campus, last_response_body, response_keys)
Expand Down
Loading