log asset accesses for API calls

to allow mobile app activity to appear on the course access report

test plan: the following API endpoints should appear on the course
access report (enable page views to test):
 - assignments index
 - assignment show
 - collaborations index
 - conferences index
 - modules index
 - course roster (/api/v1/courses/X/users)
 - discussion topics index
 - discussion show
 - discussion view ("Get the full topic")
 - pages index
 - page show
 - quizzes index
 - quiz show

for paginated index endpoints, only the first page should result
in an asset access

canvas web pages that are built on the API should not record
double accesses

flag=none

fixes ADMIN-2840

Change-Id: I7163537b05d389a0a33b0a0128740e7be354349a
Reviewed-on: https://gerrit.instructure.com/208379
Tested-by: Jenkins
Reviewed-by: Jon Willesen <jonw+gerrit@instructure.com>
QA-Review: Jon Willesen <jonw+gerrit@instructure.com>
Product-Review: Jeremy Stanley <jeremy@instructure.com>
This commit is contained in:
Jeremy Stanley 2019-09-05 14:34:51 -06:00
parent a28a006fa9
commit 17a3ae2143
10 changed files with 18 additions and 0 deletions

View File

@ -1252,6 +1252,12 @@ class ApplicationController < ActionController::Base
end
end
def log_api_asset_access(asset, asset_category, asset_group=nil, level=nil, membership_type=nil, overwrite:true)
return if in_app? # don't log duplicate accesses for API calls made by the Canvas front-end
return if params[:page].to_i > 1 # don't log duplicate accesses for pages after the first
log_asset_access(asset, asset_category, asset_group, level, membership_type, overwrite: overwrite)
end
def log_page_view
return true if !page_views_enabled?

View File

@ -712,6 +712,7 @@ class AssignmentsApiController < ApplicationController
def get_assignments(user)
if authorized_action(@context, user, :read)
log_api_asset_access([ "assignments", @context ], "assignments", "other")
scope = Assignments::ScopedToUser.new(@context, user).scope.
eager_load(:assignment_group).
preload(:rubric_association, :rubric).
@ -848,6 +849,7 @@ class AssignmentsApiController < ApplicationController
locked = @assignment.locked_for?(@current_user, :check_policies => true)
@assignment.context_module_action(@current_user, :read) unless locked && !locked[:can_view]
log_api_asset_access(@assignment, "assignments", @assignment.assignment_group)
render :json => assignment_json(@assignment, @current_user, session,
submission: submissions,

View File

@ -168,6 +168,7 @@ class CollaborationsController < ApplicationController
def api_index
return unless authorized_action(@context, @current_user, :read) &&
(tab_enabled?(@context.class::TAB_COLLABORATIONS) || tab_enabled?(@context.class::TAB_COLLABORATIONS_NEW))
log_api_asset_access([ "collaborations", @context ], "collaborations", "other")
url = @context.instance_of?(Course) ? api_v1_course_collaborations_index_url : api_v1_group_collaborations_index_url

View File

@ -163,6 +163,7 @@ class ConferencesController < ApplicationController
return unless authorized_action(@context, @current_user, :read)
return unless tab_enabled?(@context.class::TAB_CONFERENCES)
return unless @current_user
log_api_asset_access([ "conferences", @context ], "conferences", "other")
conferences = @context.grants_right?(@current_user, :manage_content) ?
@context.web_conferences.active :
@current_user.web_conferences.active.shard(@context.shard).where(context_type: @context.class.to_s, context_id: @context.id)

View File

@ -152,6 +152,7 @@ class ContextModulesApiController < ApplicationController
# @returns [Module]
def index
if authorized_action(@context, @current_user, :read)
log_api_asset_access([ "modules", @context ], "modules", "other")
route = polymorphic_url([:api_v1, @context, :context_modules])
scope = @context.modules_visible_to(@student || @current_user)

View File

@ -945,6 +945,7 @@ class CoursesController < ApplicationController
Shackles.activate(:slave) do
get_context
if authorized_action(@context, @current_user, [:read_roster, :view_all_grades, :manage_grades])
log_api_asset_access([ "roster", @context ], 'roster', 'other')
#backcompat limit param
params[:per_page] ||= params[:limit]

View File

@ -55,6 +55,7 @@ class DiscussionTopicsApiController < ApplicationController
# -H 'Authorization: Bearer <token>'
def show
include_params = Array(params[:include])
log_asset_access(@topic, 'topics', 'topics')
render(json: discussion_topics_api_json([@topic], @context,
@current_user, session,
include_all_dates: include_params.include?('all_dates'),
@ -121,6 +122,7 @@ class DiscussionTopicsApiController < ApplicationController
# }
def view
return unless authorized_action(@topic, @current_user, :read_replies)
log_asset_access(@topic, 'topics', 'topics')
mobile_brand_config = !in_app? && @context.account.effective_brand_config
opts = {

View File

@ -434,6 +434,7 @@ class DiscussionTopicsController < ApplicationController
render html: '', layout: true
end
format.json do
log_api_asset_access([ "topics", @context ], 'topics', 'other')
if @context.grants_right?(@current_user, session, :moderate_forum)
mc_status = setup_master_course_restrictions(@topics, @context)
end

View File

@ -307,6 +307,7 @@ class Quizzes::QuizzesApiController < ApplicationController
# @returns [Quiz]
def index
if authorized_action(@context, @current_user, :read) && tab_enabled?(@context.class::TAB_QUIZZES)
log_api_asset_access([ "quizzes", @context ], "quizzes", 'other')
updated = @context.quizzes.active.reorder('updated_at DESC').limit(1).pluck(:updated_at).first
cache_key = ['quizzes', @context.id, @context.quizzes.active.size,
@current_user, updated, accepts_jsonapi?,
@ -345,6 +346,7 @@ class Quizzes::QuizzesApiController < ApplicationController
# @returns Quiz
def show
if authorized_action(@quiz, @current_user, :read)
log_asset_access(@quiz, "quizzes", "quizzes")
render_json
end
end

View File

@ -241,6 +241,7 @@ class WikiPagesApiController < ApplicationController
# @returns [Page]
def index
if authorized_action(@context.wiki, @current_user, :read) && tab_enabled?(@context.class::TAB_PAGES)
log_api_asset_access([ "pages", @context ], "pages", "other")
pages_route = polymorphic_url([:api_v1, @context, :wiki_pages])
# omit body from selection, since it's not included in index results
scope = @context.wiki_pages.select(WikiPage.column_names - ['body']).preload(:user)