parent_frame_context to external_content#success

closes: INTEROP-7744
flag=none

test plan:
- Add this to ui/shared/tinymce-external-tools/react/components/ExternalToolDialog.js (after line 180)
   `<input type="hidden" name="parent_frame_context" value="{id of tool}" />`
- add a debugger statement to ui/features/external_content_success/index.js just before line 64
- Launch a 1.1 tool from RCE
- choose "return link content item to Canvas"
- when the debugger stops in the browser inspect the window of the frame, and `window.ENV.DEEP_LINKING_POST_MESSAGE_ORIGIN` should be the origin of the tool

Change-Id: Ifd281c75b259c5617cb3275f928e179ae091ae98
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/306014
Reviewed-by: Evan Battaglia <ebattaglia@instructure.com>
QA-Review: Evan Battaglia <ebattaglia@instructure.com>
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Product-Review: Paul Gray <paul.gray@instructure.com>
This commit is contained in:
Steve McGee 2022-11-23 14:42:57 -07:00 committed by Steve Mcgee
parent a5d18d91db
commit 0f1b7658c9
4 changed files with 128 additions and 10 deletions

View File

@ -23,6 +23,7 @@ IMS::LTI::Models::ContentItems::ContentItem.add_attribute :canvas_url, json_key:
class ExternalContentController < ApplicationController
include Lti::Concerns::Oembed
include Lti::Concerns::ParentFrame
protect_from_forgery except: [:selection_test, :success], with: :exception
@ -87,6 +88,12 @@ class ExternalContentController < ApplicationController
error_message: param_if_set(:lti_errormsg),
error_log: param_if_set(:lti_errorlog)
})
tool_origin = parent_frame_origin(params[:parent_frame_context])
if tool_origin
js_env({
DEEP_LINKING_POST_MESSAGE_ORIGIN: tool_origin
}, true)
end
end
def normalize_deprecated_data!

View File

@ -0,0 +1,43 @@
# frozen_string_literal: true
#
# Copyright (C) 2020 - present Instructure, Inc.
#
# This file is part of Canvas.
#
# Canvas is free software: you can redistribute it and/or modify it under
# the terms of the GNU Affero General Public License as published by the Free
# Software Foundation, version 3 of the License.
#
# Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
# details.
#
# You should have received a copy of the GNU Affero General Public License along
# with this program. If not, see <http://www.gnu.org/licenses/>.
module Lti::Concerns
module ParentFrame
extend ActiveSupport::Concern
# Takes an id of a tool and returns the origin of that tool if it exists
# otherwise returns nil
def parent_frame_origin(tool_id)
tool = tool_id ? ContextExternalTool.find_by(id: tool_id) : nil
return nil unless tool&.active? && tool&.developer_key&.internal_service
if tool.url
override_parent_frame_origin(tool.url)
elsif tool.domain
"https://#{tool.domain}"
end
end
def override_parent_frame_origin(url)
uri = URI.parse(url)
origin = URI("#{uri.scheme}://#{uri.host}:#{uri.port}")
origin.to_s
end
end
end

View File

@ -26,6 +26,7 @@ module Lti
include Lti::IMS::Concerns::DeepLinkingServices
include Lti::IMS::Concerns::DeepLinkingModules
include Lti::Concerns::ParentFrame
before_action :require_context
before_action :validate_jwt
@ -153,23 +154,16 @@ module Lti
moduleCreated: module_created
}.compact
})
parent_frame_context = return_url_parameters[:parent_frame_context]
parent_frame_tool = parent_frame_context ? ContextExternalTool.find_by(id: parent_frame_context) : nil
if parent_frame_tool&.active? && parent_frame_tool&.developer_key&.internal_service
tool_origin = parent_frame_origin(return_url_parameters[:parent_frame_context])
if tool_origin
js_env({
DEEP_LINKING_POST_MESSAGE_ORIGIN: origin(parent_frame_tool.url)
DEEP_LINKING_POST_MESSAGE_ORIGIN: tool_origin
}, true)
end
render layout: "bare"
end
def origin(url)
uri = URI.parse(url)
origin = URI("#{uri.scheme}://#{uri.host}:#{uri.port}")
origin.to_s
end
def require_context_update_rights
return unless create_resources_from_content_items?

View File

@ -101,6 +101,80 @@ describe ExternalContentController do
lti_errorlog: '{"html"=>"errorlog somehtml"}'
)
end
describe "DEEP_LINKING_POST_MESSAGE_ORIGIN" do
subject do
post(
:success,
params: {
service: "external_tool_dialog",
course_id: c.id,
parent_frame_context: tool.id
}
)
end
let(:tool) do
c.context_external_tools.create!(
{
name: "test tool",
domain: "test.com",
consumer_key: "fake_oauth_consumer_key",
shared_secret: "secret",
developer_key: developer_key,
url: "http://test.com/login",
}
)
end
let(:developer_key) do
key = DeveloperKey.new
key.generate_rsa_keypair!
key.save!
key.developer_key_account_bindings.first.update!(
workflow_state: "on"
)
key
end
context "when returning from a non-internal service" do
it "does not set the DEEP_LINKING_POST_MESSAGE_ORIGIN value in jsenv" do
expect(controller).not_to receive(:js_env).with({ DEEP_LINKING_POST_MESSAGE_ORIGIN: "http://test.com" }, true)
subject
end
end
context "when returning from an internal service" do
before do
developer_key.update!(internal_service: true)
end
it "sets the DEEP_LINKING_POST_MESSAGE_ORIGIN value in jsenv" do
allow(controller).to receive(:js_env)
subject
expect(controller).to have_received(:js_env).with({ DEEP_LINKING_POST_MESSAGE_ORIGIN: "http://test.com" }, true)
end
context "when the tool has a domain and not a url" do
let(:tool) do
c.context_external_tools.create!(
{
name: "test tool",
domain: "test.com",
consumer_key: "fake_oauth_consumer_key",
shared_secret: "secret",
developer_key: developer_key,
}
)
end
it "sets the DEEP_LINKING_POST_MESSAGE_ORIGIN value in jsenv" do
allow(controller).to receive(:js_env)
subject
expect(controller).to have_received(:js_env).with({ DEEP_LINKING_POST_MESSAGE_ORIGIN: "https://test.com" }, true)
end
end
end
end
end
context "external_tool service_id" do