Skip to content

Commit bd1da89

Browse files
authored
Merge pull request #173 from OpenDSA/lti13
Adding LTI 1.3 Support
2 parents f1e322a + 048c6e5 commit bd1da89

31 files changed

+1386
-549
lines changed

app/admin/lms_access.rb

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
end
2525

2626
menu label: "LMS Accesses", parent: 'LMS config', priority: 30
27-
permit_params :lms_instance_id, :user_id, :access_token
27+
permit_params :lms_instance_id, :user_id, :access_token, :consumer_key
2828

2929
index do
3030
id_column
@@ -45,6 +45,7 @@
4545
link_to c.access_token, admin_lms_access_path(c)
4646
end
4747
# column :created_at
48+
column :consumer_key
4849
actions
4950
end
5051

@@ -56,6 +57,7 @@
5657
f.input :user, collection: User.all.order(:first_name, :last_name)
5758
end
5859
f.input :access_token
60+
f.input :consumer_key
5961
end
6062
f.actions
6163
end

app/admin/lms_instance.rb

+20-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,13 @@
1010
# consumer_key :string(255)
1111
# consumer_secret :string(255)
1212
# organization_id :bigint
13+
# client_id :string
14+
# private_key :text
15+
# public_key :text
16+
# keyset_url :string
17+
# oauth2_url :string
18+
# platform_oidc_auth_url :string
19+
# issuer :string
1320
#
1421
# Indexes
1522
#
@@ -21,7 +28,9 @@
2128
includes :lms_type, :organization
2229

2330
menu label: "LMS Instances",parent: 'LMS config', priority: 20
24-
permit_params :url, :lms_type_id, :organization_id
31+
# permit_params :url, :lms_type_id, :organization_id
32+
permit_params :url, :lms_type_id, :organization_id, :client_id, :private_key, :public_key, :keyset_url, :oauth2_url, :platform_oidc_auth_url, :issuer
33+
2534

2635
index do
2736
id_column
@@ -31,6 +40,14 @@
3140
# column :consumer_key
3241
# column :consumer_secret
3342
column :created_at
43+
column :client_id
44+
# Consider if you really want to display keys and secrets here
45+
# column :private_key
46+
# column :public_key
47+
column :keyset_url
48+
column :oauth2_url
49+
column :platform_oidc_auth_url
50+
column :issuer
3451
actions
3552
end
3653

@@ -43,8 +60,8 @@
4360
f.input :consumer_key
4461
f.input :consumer_secret
4562
f.input :client_id
46-
f.input :private_key
47-
f.input :public_key
63+
f.input :private_key, as: :text
64+
f.input :public_key, as: :text
4865
f.input :keyset_url
4966
f.input :oauth2_url
5067
f.input :platform_oidc_auth_url

app/controllers/inst_books_controller.rb

+18-2
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,25 @@ class InstBooksController < ApplicationController
88
def compile
99
host_port = request.protocol + request.host_with_port
1010
extrtool_launch_base_url = host_port + "/lti/launch_extrtool"
11+
inst_book = InstBook.find(params[:id])
12+
course_offering = inst_book.course_offering
13+
lms_instance = course_offering.lms_instance
14+
15+
# Determine LTI version from the LmsInstance
16+
lti_version = lms_instance.lti_version
17+
puts "Determined LTI version: #{lti_version}"
18+
# Set URLs based on the LTI version
1119
if params[:operation] == 'generate_course'
12-
launch_url = host_port + "/lti/launch"
13-
resource_selection_url = host_port + "/lti/resource"
20+
if lti_version == 'LTI-1p0'
21+
launch_url = host_port + "/lti/launch"
22+
resource_selection_url = host_port + "/lti/resource"
23+
elsif lti_version == 'LTI-1p3'
24+
launch_url = host_port + "/lti13/launches"
25+
resource_selection_url = host_port + "/lti13/deep_linking/content_selection"
26+
else
27+
render plain: "Unsupported LTI version", status: :unprocessable_entity
28+
return
29+
end
1430
@job = Delayed::Job.enqueue GenerateCourseJob.new(params[:id], launch_url, resource_selection_url,
1531
extrtool_launch_base_url, current_user.id)
1632
else
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,77 @@
11
class Lti13::DeepLinkLaunchesController < ApplicationController
2-
before_action :set_tool
3-
skip_before_action only: :create
4-
5-
# POST lti/tools/#/deep_link_launches
6-
# Not much different than LTI launch endpoint inside a simple reference implementation but you
7-
# should have a diff endpoint for deeplinks than your LTI resource link request for single responsiblity
8-
def create
9-
if params[:id_token]&.present?
10-
@decoded_header = Jwt::Header.new(params[:id_token]).call
11-
kid = @decoded_header['kid']
12-
13-
@decoded_jwt = Lti13Service::DecodePlatformJwt.new(@tool, params[:id_token], kid).call
14-
@launch = @tool.launches.build(jwt: params[:id_token], decoded_jwt: @decoded_jwt ? @decoded_jwt.first : nil, state: params[:state])
15-
end
16-
17-
@launch ||= Launch.new
18-
respond_to do |format|
19-
if @launch.save
20-
format.html { redirect_to [:lti, @tool, @launch], notice: 'Successful Launch.' }
21-
format.json { render :show, status: :created, location: @launch }
22-
else
23-
format.html { render json: 'Invalid Launch', status: :unprocessable_entity }
24-
format.json { render json: @launch.errors, status: :unprocessable_entity }
25-
end
26-
end
27-
end
28-
29-
# GET lti/tools/#/deep_link_launch/*launch_id*
30-
# page that allows user to select content
31-
def show
32-
@launch = Launch.find(params[:id])
2+
before_action :set_tool
3+
skip_before_action only: :create
4+
5+
# POST lti/tools/#/deep_link_launches
6+
# Handles the creation of a deep link launch
7+
def create
8+
if params[:id_token]&.present?
9+
@decoded_header = Jwt::Header.new(params[:id_token]).call
10+
kid = @decoded_header['kid']
11+
12+
@decoded_jwt = Lti13Service::DecodePlatformJwt.new(@tool, params[:id_token], kid).call
13+
@launch = @tool.launches.build(jwt: params[:id_token], decoded_jwt: @decoded_jwt ? @decoded_jwt.first : nil, state: params[:state])
3314
end
34-
35-
# GET lti/tools/#/deep_link_launch/*launch_id*/launch
36-
# takes selected content and launches back to platform with JWT
37-
def launch
38-
@launch = Launch.find(params[:deep_link_launch_id])
39-
@form_url = @launch.decoded_jwt[Rails.configuration.lti_claims_and_scopes['deep_linking_claim']]['deep_link_return_url']
40-
@deep_link_jwt = Lti13Service::DeepLinkJwt.new(@launch, lti_tool_launches_url(@tool), params[:content_items])
41-
end
42-
43-
private
44-
def set_tool
45-
@tool = Tool.find_by_id(params[:tool_id])
46-
render json: { error: 'Tool not found' }, status: :not_found unless @tool
15+
16+
@launch ||= Launch.new
17+
respond_to do |format|
18+
if @launch.save
19+
format.html { redirect_to [:lti13, @tool, @launch], notice: 'Successful Launch.' }
20+
format.json { render :show, status: :created, location: @launch }
21+
else
22+
format.html { render json: 'Invalid Launch', status: :unprocessable_entity }
23+
format.json { render json: @launch.errors, status: :unprocessable_entity }
4724
end
48-
end
25+
end
26+
end
27+
28+
# GET lti/tools/#/deep_link_launch/*launch_id*
29+
# allows user to select content
30+
def show
31+
@launch = Launch.find(params[:id])
32+
end
33+
34+
# GET lti/tools/#/deep_link_launch/*launch_id*/launch
35+
# takes selected content and launches back to platform with JWT
36+
def launch
37+
@launch = Launch.find(params[:deep_link_launch_id])
38+
@form_url = @launch.decoded_jwt[Rails.configuration.lti_claims_and_scopes['deep_linking_claim']]['deep_link_return_url']
39+
@deep_link_jwt = Lti13Service::DeepLinkJwt.new(@launch, lti_tool_launches_url(@tool), params[:content_items])
40+
end
41+
42+
# GET lti13/deep_linking/content_selection
43+
def content_selection
44+
@launch_url = request.protocol + request.host_with_port + "/lti13/launches"
45+
module_info = InstModule.get_current_versions_dict()
46+
@json = module_info.to_json
47+
48+
Rails.logger.info "Launch URL: #{@launch_url}"
49+
Rails.logger.debug "Module Info JSON: #{@json.inspect}"
50+
render 'resource', layout: 'lti_resource'
51+
end
52+
53+
# POST lti13/deep_linking/content_selected
54+
def content_selected
55+
@launch = Launch.find(params[:launch_id])
56+
@form_url = @launch.decoded_jwt[Rails.configuration.lti_claims_and_scopes['deep_linking_claim']]['deep_link_return_url']
57+
selected_content = params[:selected_content]
58+
Rails.logger.info "Selected Content: #{selected_content}"
59+
60+
deep_link_jwt_service = Lti13Service::DeepLinkJwt.new(@launch, selected_content)
61+
deep_link_jwt = deep_link_jwt_service.call
62+
Rails.logger.info "Deep Link JWT: #{deep_link_jwt}"
63+
64+
# Return the selected content to LMS
65+
redirect_to "#{@form_url}?JWT=#{deep_link_jwt}"
66+
end
67+
68+
#~ Private methods ..........................................................
69+
70+
private
71+
# -------------------------------------------------------------
72+
73+
def set_tool
74+
@tool = Tool.find_by_id(params[:tool_id])
75+
render json: { error: 'Tool not found' }, status: :not_found unless @tool
76+
end
77+
end

0 commit comments

Comments
 (0)