panda_pal 5.15.1.beta3 → 5.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/MIT-LICENSE +0 -0
- data/README.md +38 -2
- data/Rakefile +0 -0
- data/app/assets/config/panda_pal_manifest.js +0 -0
- data/app/assets/javascripts/panda_pal/lti.js +0 -0
- data/app/assets/stylesheets/panda_pal/application.css +0 -0
- data/app/assets/stylesheets/panda_pal/lti.css +0 -0
- data/app/controllers/panda_pal/lti_v1_p3_controller.rb +28 -3
- data/app/helpers/panda_pal/application_helper.rb +0 -0
- data/app/jobs/panda_pal/jobs/grade_passback_job.rb +0 -0
- data/app/lib/lti_xml/base_platform.rb +0 -0
- data/app/lib/lti_xml/bridge_platform.rb +0 -0
- data/app/lib/lti_xml/canvas_platform.rb +0 -0
- data/app/lib/panda_pal/deep_linking_helpers.rb +98 -0
- data/app/lib/panda_pal/launch_url_helpers.rb +37 -0
- data/app/lib/panda_pal/lti_jwt_validator.rb +0 -0
- data/app/models/panda_pal/api_call.rb +0 -0
- data/app/models/panda_pal/organization_concerns/organization_builder.rb +0 -0
- data/app/models/panda_pal/organization_concerns/settings_validation.rb +0 -0
- data/app/models/panda_pal/organization_concerns/task_scheduling.rb +0 -0
- data/app/models/panda_pal/panda_pal_record.rb +0 -0
- data/app/models/panda_pal/platform.rb +0 -0
- data/app/models/panda_pal/session.rb +1 -1
- data/app/views/layouts/panda_pal/application.html.erb +0 -0
- data/app/views/panda_pal/lti/launch.html.erb +0 -0
- data/app/views/panda_pal/lti_v1_p3/login.html.erb +0 -0
- data/app/views/panda_pal/partials/_auto_submit_form.html.erb +0 -0
- data/config/dev_lti_key.key +0 -0
- data/config/initializers/apartment.rb +21 -31
- data/config/routes.rb +1 -0
- data/db/migrate/20160412205931_create_panda_pal_organizations.rb +0 -0
- data/db/migrate/20160413135653_create_panda_pal_sessions.rb +0 -0
- data/db/migrate/20160425130344_add_panda_pal_organization_to_session.rb +0 -0
- data/db/migrate/20170106165533_add_salesforce_id_to_organizations.rb +0 -0
- data/db/migrate/20171205183457_encrypt_organization_settings.rb +0 -0
- data/db/migrate/20171205194657_remove_old_organization_settings.rb +0 -0
- data/db/migrate/20220721095653_create_panda_pal_api_calls.rb +0 -0
- data/db/migrate/20250401214421_rename_session_session_key.rb +1 -1
- data/lib/panda_pal/concerns/ability_helper.rb +0 -0
- data/lib/panda_pal/helpers/console_helpers.rb +0 -0
- data/lib/panda_pal/helpers/misc_helper.rb +0 -0
- data/lib/panda_pal/helpers/route_helper.rb +0 -0
- data/lib/panda_pal/helpers/secure_headers.rb +0 -0
- data/lib/panda_pal/helpers.rb +0 -0
- data/lib/panda_pal/lti_constants.rb +48 -0
- data/lib/panda_pal/plugins.rb +0 -0
- data/lib/panda_pal/version.rb +1 -1
- data/lib/panda_pal.rb +18 -4
- data/lib/tasks/panda_pal_tasks.rake +0 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c78eb783dcf56cb075ce23d1218500128e6ac1ac58c3049bcfa13f6409835554
|
4
|
+
data.tar.gz: bb555a130fdab3e1805013700e04bddfe24a6c3460750ac2201a06f9dfe96c1d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5378ea91ce63e263dc93368f4d87cec35a14273b74f37452cec2b1dea67ff8bbb3a261e609bbc550bbcc4f08dac8c1de09e387e6ec8691e3d4901ac7b2af1e0a
|
7
|
+
data.tar.gz: aef30aa2d98b20f502a9274ce31c5bf17e66111c8a628cd61ce2765c06ef7fe280af459069a3fafe0a13aa885e9ba915f048553f14714e3ad274537b7bd4cfe3
|
data/MIT-LICENSE
CHANGED
File without changes
|
data/README.md
CHANGED
@@ -149,6 +149,42 @@ end
|
|
149
149
|
the defined action. Setting it to `false` indicates that the defined action handles launch validation and setup itself (this has been the legacy approach).
|
150
150
|
Because `auto_launch: false` is most similar to the previous behavior, it is the default for LTI 1.0/1.1 LTIs. For LTI 1.3 LTIs, `auto_launch: true` is the default. If not specified and `:organization_id` is detected in the Route Path, `auto_launch` will be set to `true`
|
151
151
|
|
152
|
+
## Deep Linking (LTI 1.3)
|
153
|
+
|
154
|
+
PandaPal provides comprehensive support for LTI 1.3 Deep Linking, allowing tools to be configured for content selection that can be placed directly into Canvas courses.
|
155
|
+
|
156
|
+
### Quick Start
|
157
|
+
|
158
|
+
1. **Configure placements** in your LTI initializer:
|
159
|
+
```ruby
|
160
|
+
# config/initializers/lti.rb
|
161
|
+
PandaPal.stage_placement(:content_selection, {
|
162
|
+
deep_linking: true,
|
163
|
+
deep_link_redirect_url: :content_selection_url,
|
164
|
+
text: 'Select Content',
|
165
|
+
enabled: true
|
166
|
+
})
|
167
|
+
```
|
168
|
+
|
169
|
+
2. **Include helpers** in your controller and build content items:
|
170
|
+
```ruby
|
171
|
+
class ContentSelectionController < ApplicationController
|
172
|
+
include PandaPal::DeepLinkingHelpers
|
173
|
+
|
174
|
+
def create
|
175
|
+
content_items = [
|
176
|
+
build_link_content_item(
|
177
|
+
url: launch_url_for_content(params[:content_id]),
|
178
|
+
title: 'Selected Content'
|
179
|
+
)
|
180
|
+
]
|
181
|
+
render_deep_linking_response(content_items)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
```
|
185
|
+
|
186
|
+
**For complete configuration options, implementation examples, helper method documentation, and troubleshooting, see [DEEP_LINKING.md](DEEP_LINKING.md).**
|
187
|
+
|
152
188
|
## Implementating data segregation
|
153
189
|
This engine uses Apartment to keep data segregated between installations of the implementing LTI tool.
|
154
190
|
By default, it does this by inspecting the path of the request, and matching URLs containing `orgs` or `organizations`,
|
@@ -457,7 +493,7 @@ data_type: If specified, and a settings hash contains this attribute, attribute
|
|
457
493
|
use `class` to determine data type. For example: 30.minutes.class.to_s => "ActiveSupport::Duration"
|
458
494
|
|
459
495
|
## Bridge vs Canvas
|
460
|
-
As of 3.2.0, the LTI XML config can have subtle differences based on whether your platform is bridge, or canvas.
|
496
|
+
As of 3.2.0, the LTI XML config can have subtle differences based on whether your platform is bridge, or canvas.
|
461
497
|
This is determined by `PandaPal.lti_options[:platform]`.
|
462
498
|
Set this to `platform: 'canvas.instructure.com'` (default)
|
463
499
|
OR `platform: 'bridgeapp.com'`
|
@@ -490,7 +526,7 @@ You will want to watch out for a few scenarios:
|
|
490
526
|
2) Use the `Authorization` header with `token={session_key}` to send your
|
491
527
|
PandaPal session info into api calls.
|
492
528
|
3) If you use `link_to` and navigate in your LTI (apps that are not single page)
|
493
|
-
make sure you include the `link_nonce` like so:
|
529
|
+
make sure you include the `link_nonce` like so:
|
494
530
|
```ruby
|
495
531
|
link_to "Link Name", somewhere_else_path(arg, session_token: link_nonce)
|
496
532
|
```
|
data/Rakefile
CHANGED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -6,7 +6,7 @@ module PandaPal
|
|
6
6
|
skip_before_action :verify_authenticity_token, raise: false
|
7
7
|
end
|
8
8
|
|
9
|
-
before_action ->{ validate_launch!(version: :v1p3) }, only: [:resource_link_request]
|
9
|
+
before_action ->{ validate_launch!(version: :v1p3) }, only: [:resource_link_request, :deep_link_request]
|
10
10
|
before_action :enforce_environment!, only: [:resource_link_request]
|
11
11
|
|
12
12
|
# Redirect to beta/test as necessary
|
@@ -40,7 +40,7 @@ module PandaPal
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def resource_link_request
|
43
|
-
ltype = @decoded_lti_jwt[
|
43
|
+
ltype = @decoded_lti_jwt[LtiConstants::Canvas::PLACEMENT]
|
44
44
|
|
45
45
|
if ltype
|
46
46
|
current_session.data.merge!({
|
@@ -54,6 +54,32 @@ module PandaPal
|
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
|
+
def deep_link_request
|
58
|
+
message_type = @decoded_lti_jwt[LtiConstants::Claims::MESSAGE_TYPE]
|
59
|
+
|
60
|
+
unless message_type == LtiConstants::MessageTypes::DEEP_LINKING_REQUEST
|
61
|
+
render plain: 'Not a Deep Linking request', status: :bad_request
|
62
|
+
return
|
63
|
+
end
|
64
|
+
|
65
|
+
# Store deep linking session data
|
66
|
+
current_session.data.merge!({
|
67
|
+
lti_version: 'v1p3',
|
68
|
+
lti_launch_placement: 'deep_linking',
|
69
|
+
launch_url_params: request.query_parameters.to_h,
|
70
|
+
launch_params: @decoded_lti_jwt,
|
71
|
+
deep_link_return_url: @decoded_lti_jwt[LtiConstants::DeepLinking::DEEP_LINKING_SETTINGS][LtiConstants::DeepLinkingSettings::DEEP_LINK_RETURN_URL],
|
72
|
+
deep_link_data: @decoded_lti_jwt[LtiConstants::DeepLinking::DATA]
|
73
|
+
})
|
74
|
+
|
75
|
+
redirect_url = LaunchUrlHelpers.deep_link_redirect_url(request)
|
76
|
+
if redirect_url
|
77
|
+
redirect_with_session_to(redirect_url, route_context: main_app)
|
78
|
+
else
|
79
|
+
render plain: 'Deep linking redirect URL not configured. Please configure deep_link_redirect_url in your placement or global LTI options.', status: :internal_server_error
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
57
83
|
def tool_config
|
58
84
|
if PandaPal.lti_environments.empty?
|
59
85
|
render plain: 'Domains must be set in lti_environments'
|
@@ -71,7 +97,6 @@ module PandaPal
|
|
71
97
|
target_link_uri: LaunchUrlHelpers.absolute_launch_url(
|
72
98
|
k.to_sym,
|
73
99
|
host: parsed_request_url,
|
74
|
-
launch_handler: v1p3_resource_link_request_path,
|
75
100
|
default_auto_launch: true
|
76
101
|
),
|
77
102
|
})
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -0,0 +1,98 @@
|
|
1
|
+
module PandaPal
|
2
|
+
module DeepLinkingHelpers
|
3
|
+
# Build a link content item
|
4
|
+
def build_link_content_item(url:, title: nil, text: nil, icon: nil, thumbnail: nil, **options)
|
5
|
+
content_item = {
|
6
|
+
type: 'link',
|
7
|
+
url: url,
|
8
|
+
title: title
|
9
|
+
}
|
10
|
+
content_item[:text] = text if text.present?
|
11
|
+
content_item[:icon] = icon if icon.present?
|
12
|
+
content_item[:thumbnail] = thumbnail if thumbnail.present?
|
13
|
+
content_item.merge(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
# Build a file content item
|
17
|
+
def build_file_content_item(url:, title: nil, text: nil, media_type: nil, icon: nil, **options)
|
18
|
+
content_item = {
|
19
|
+
type: 'file',
|
20
|
+
url: url,
|
21
|
+
title: title
|
22
|
+
}
|
23
|
+
content_item[:text] = text if text.present?
|
24
|
+
content_item[:mediaType] = media_type if media_type.present?
|
25
|
+
content_item[:icon] = icon if icon.present?
|
26
|
+
content_item.merge(options)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Build an HTML content item
|
30
|
+
def build_html_content_item(html:, title: nil, text: nil, **options)
|
31
|
+
content_item = {
|
32
|
+
type: 'html',
|
33
|
+
html: html,
|
34
|
+
title: title
|
35
|
+
}
|
36
|
+
content_item[:text] = text if text.present?
|
37
|
+
content_item.merge(options)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Render the deep linking response
|
41
|
+
# This builds the JWT and renders the auto-submit form
|
42
|
+
def render_deep_linking_response(content_items, custom_data: {})
|
43
|
+
deep_link_return_url = current_session[:deep_link_return_url]
|
44
|
+
deep_link_data = current_session[:deep_link_data]
|
45
|
+
|
46
|
+
unless deep_link_return_url.present?
|
47
|
+
render plain: 'No deep linking session found. Please ensure you accessed this through a proper deep linking flow.', status: :bad_request
|
48
|
+
return
|
49
|
+
end
|
50
|
+
|
51
|
+
# Build custom deep link data - merge Canvas data with custom data
|
52
|
+
launch_params = current_session[:launch_params]
|
53
|
+
merged_custom_data = (deep_link_data || {}).merge({
|
54
|
+
selection_time: Time.now.iso8601,
|
55
|
+
selected_by: launch_params&.dig('sub') || 'unknown'
|
56
|
+
}).merge(custom_data)
|
57
|
+
|
58
|
+
# Build the JWT response
|
59
|
+
jwt = build_deep_link_jwt(content_items, merged_custom_data)
|
60
|
+
|
61
|
+
# Set up the auto-submit form
|
62
|
+
@form_action = deep_link_return_url
|
63
|
+
@method = :post
|
64
|
+
@form_data = { JWT: jwt }
|
65
|
+
|
66
|
+
render partial: 'panda_pal/partials/auto_submit_form'
|
67
|
+
end
|
68
|
+
|
69
|
+
private
|
70
|
+
|
71
|
+
def build_deep_link_jwt(content_items, custom_deep_link_data)
|
72
|
+
# Get issuer (client_id) and audience from session
|
73
|
+
launch_params = current_session[:launch_params]
|
74
|
+
issuer = launch_params&.dig('aud') || current_organization.key
|
75
|
+
audience = launch_params&.dig('iss')
|
76
|
+
|
77
|
+
unless audience.present?
|
78
|
+
raise StandardError, 'Unable to determine audience for deep linking response'
|
79
|
+
end
|
80
|
+
|
81
|
+
deep_link_jwt_payload = {
|
82
|
+
iss: issuer,
|
83
|
+
aud: audience,
|
84
|
+
iat: Time.now.to_i,
|
85
|
+
exp: Time.now.to_i + 600,
|
86
|
+
nonce: SecureRandom.uuid,
|
87
|
+
PandaPal::LtiConstants::Claims::MESSAGE_TYPE => PandaPal::LtiConstants::MessageTypes::DEEP_LINKING_RESPONSE,
|
88
|
+
PandaPal::LtiConstants::Claims::VERSION => PandaPal::LtiConstants::Versions::V1_3,
|
89
|
+
PandaPal::LtiConstants::DeepLinking::CONTENT_ITEMS => content_items,
|
90
|
+
PandaPal::LtiConstants::DeepLinking::DATA => custom_deep_link_data,
|
91
|
+
}
|
92
|
+
|
93
|
+
require 'json/jwt'
|
94
|
+
jwk = JSON::JWK.new(PandaPal.lti_private_key)
|
95
|
+
JSON::JWT.new(deep_link_jwt_payload).sign(jwk, :RS256).to_s
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -2,6 +2,13 @@ module PandaPal
|
|
2
2
|
module LaunchUrlHelpers
|
3
3
|
def self.absolute_launch_url(launch_type, host: uri_host, launch_handler: nil, default_auto_launch: false)
|
4
4
|
opts = PandaPal.lti_paths[launch_type]
|
5
|
+
|
6
|
+
if launch_handler.nil? && opts.present?
|
7
|
+
launch_handler = opts[:message_type] == PandaPal::LtiConstants::MessageTypes::DEEP_LINKING_REQUEST ?
|
8
|
+
:v1p3_deep_link_request_path :
|
9
|
+
:v1p3_resource_link_request_path
|
10
|
+
end
|
11
|
+
|
5
12
|
auto_launch = opts[:auto_launch] != nil ? opts[:auto_launch] : default_auto_launch
|
6
13
|
auto_launch = auto_launch && launch_handler.present?
|
7
14
|
|
@@ -100,5 +107,35 @@ module PandaPal
|
|
100
107
|
engine = key_bits[0] == 'MainApp' ? Rails.application : (key_bits[0].constantize)::Engine
|
101
108
|
engine.routes.url_helpers.send(key_bits[1], *arguments, **kwargs)
|
102
109
|
end
|
110
|
+
|
111
|
+
def self.deep_link_redirect_url(request)
|
112
|
+
# Try placement-specific configuration first
|
113
|
+
launch_type = request.query_parameters['launch_type']&.to_sym
|
114
|
+
return global_configuration_url unless launch_type
|
115
|
+
|
116
|
+
placement_config = PandaPal.lti_paths[launch_type]
|
117
|
+
return global_configuration_url unless deep_linking_placement?(placement_config)
|
118
|
+
|
119
|
+
# Check for explicit deep_link_redirect_url configuration
|
120
|
+
redirect_url = placement_config[:deep_link_redirect_url]
|
121
|
+
return normalize_redirect_url(redirect_url) if redirect_url.present?
|
122
|
+
|
123
|
+
# Fall back to global configuration
|
124
|
+
global_configuration_url
|
125
|
+
end
|
126
|
+
|
127
|
+
def self.global_configuration_url
|
128
|
+
redirect_url = PandaPal.lti_options[:deep_link_redirect_url]
|
129
|
+
normalize_redirect_url(redirect_url) if redirect_url.present?
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.deep_linking_placement?(placement_config)
|
133
|
+
placement_config&.dig(:message_type) == PandaPal::LtiConstants::MessageTypes::DEEP_LINKING_REQUEST
|
134
|
+
end
|
135
|
+
|
136
|
+
def self.normalize_redirect_url(url)
|
137
|
+
return nil unless url.present?
|
138
|
+
url.is_a?(Symbol) ? url : url.to_sym
|
139
|
+
end
|
103
140
|
end
|
104
141
|
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
@@ -199,7 +199,7 @@ module PandaPal
|
|
199
199
|
def custom_lti_params
|
200
200
|
@custom_lti_params ||= begin
|
201
201
|
# LT 1.3
|
202
|
-
custom_params = launch_params[
|
202
|
+
custom_params = launch_params[PandaPal::LtiConstants::Claims::CUSTOM]
|
203
203
|
return custom_params if custom_params.present?
|
204
204
|
|
205
205
|
# LTI 1.0/1.1
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/config/dev_lti_key.key
CHANGED
File without changes
|
@@ -10,29 +10,25 @@ require "apartment/adapters/postgresql_adapter"
|
|
10
10
|
module Apartment
|
11
11
|
SHARD_PREFIXES = ["SHARD_DB", "HEROKU_POSTGRESQL"]
|
12
12
|
|
13
|
-
|
14
|
-
|
13
|
+
def self.shard_configurations
|
14
|
+
$shard_configurations ||= begin
|
15
|
+
shard_to_env = {}
|
15
16
|
|
16
|
-
|
17
|
-
$shard_configurations ||= begin
|
18
|
-
shard_to_env = {}
|
17
|
+
base_config = ActiveRecord::Base.configurations.find_db_config(Rails.env).configuration_hash
|
19
18
|
|
20
|
-
|
19
|
+
ENV.keys.each do |k|
|
20
|
+
m = /^(#{SHARD_PREFIXES.join("|")})_(\w+)_URL$/.match(k)
|
21
|
+
next unless m
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
shardcfg = ActiveRecord::Base.configurations.resolve(url).configuration_hash
|
28
|
-
shardcfg = base_config.merge(shardcfg || {})
|
29
|
-
shard_to_env[m[2].downcase] = shardcfg
|
30
|
-
end
|
23
|
+
url = ENV[k]
|
24
|
+
shardcfg = ActiveRecord::Base.configurations.resolve(url).configuration_hash
|
25
|
+
shardcfg = base_config.merge(shardcfg || {})
|
26
|
+
shard_to_env[m[2].downcase] = shardcfg
|
27
|
+
end
|
31
28
|
|
32
|
-
|
29
|
+
shard_to_env.freeze unless Rails.env.test?
|
33
30
|
|
34
|
-
|
35
|
-
end
|
31
|
+
shard_to_env
|
36
32
|
end
|
37
33
|
end
|
38
34
|
|
@@ -124,12 +120,14 @@ module Apartment
|
|
124
120
|
|
125
121
|
raise ActiveRecord::StatementInvalid, "PandaPal/Apartment Mutli-DB support does not currently support DB roles" if ActiveRecord::Base.current_role != ActiveRecord::Base.default_role
|
126
122
|
|
127
|
-
|
128
|
-
|
129
|
-
|
123
|
+
unless ActiveRecord::Base.connected?
|
124
|
+
Apartment.establish_connection multi_tenantify(tenant, false)
|
125
|
+
Apartment.connection.verify!
|
126
|
+
end
|
127
|
+
|
130
128
|
Apartment.connection.enable_query_cache! if query_cache_enabled
|
131
129
|
|
132
|
-
raise ActiveRecord::StatementInvalid, "Could not find schema for tenant
|
130
|
+
raise ActiveRecord::StatementInvalid, "Could not find schema for tenant #{tenant} (#{tenant_schemas.inspect})" unless schema_exists?(tenant_schemas)
|
133
131
|
|
134
132
|
Apartment.connection.schema_search_path = full_search_path
|
135
133
|
rescue *rescuable_exceptions => e
|
@@ -152,14 +150,6 @@ module Apartment
|
|
152
150
|
schema
|
153
151
|
end
|
154
152
|
end
|
155
|
-
|
156
|
-
def raise_schema_connect_to_new(tenant, exception)
|
157
|
-
shard, schema = Tenant.split_tenant(tenant)
|
158
|
-
raise TenantNotFound, <<~EXCEPTION_MESSAGE
|
159
|
-
Could not set search path to schemas, they may be invalid: "#{schema}" #{full_search_path}.
|
160
|
-
Original error: #{exception.class}: #{exception}
|
161
|
-
EXCEPTION_MESSAGE
|
162
|
-
end
|
163
153
|
end
|
164
154
|
end
|
165
155
|
|
@@ -220,7 +210,7 @@ Apartment.configure do |config|
|
|
220
210
|
shard_configurations = Apartment.shard_configurations
|
221
211
|
|
222
212
|
PandaPal::Organization.all.to_a.each_with_object({}) do |org, hash|
|
223
|
-
shard = org.shard
|
213
|
+
shard = org.shard || "default"
|
224
214
|
hash[org.tenant_name] = shard == "default" ? base_config : shard_configurations[shard.downcase]
|
225
215
|
end
|
226
216
|
else
|
data/config/routes.rb
CHANGED
@@ -14,6 +14,7 @@ PandaPal::Engine.routes.draw do
|
|
14
14
|
post '/oidc_login' => 'lti_v1_p3#login'
|
15
15
|
get '/oidc_login' => 'lti_v1_p3#login'
|
16
16
|
post '/resource_link_request' => 'lti_v1_p3#resource_link_request'
|
17
|
+
post '/deep_link_request' => 'lti_v1_p3#deep_link_request'
|
17
18
|
get '/public_jwks' => 'lti_v1_p3#public_jwks'
|
18
19
|
end
|
19
20
|
end
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/panda_pal/helpers.rb
CHANGED
File without changes
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module PandaPal
|
2
|
+
module LtiConstants
|
3
|
+
# LTI 1.3 Standard Claims
|
4
|
+
module Claims
|
5
|
+
MESSAGE_TYPE = 'https://purl.imsglobal.org/spec/lti/claim/message_type'.freeze
|
6
|
+
VERSION = 'https://purl.imsglobal.org/spec/lti/claim/version'.freeze
|
7
|
+
DEPLOYMENT_ID = 'https://purl.imsglobal.org/spec/lti/claim/deployment_id'.freeze
|
8
|
+
TARGET_LINK_URI = 'https://purl.imsglobal.org/spec/lti/claim/target_link_uri'.freeze
|
9
|
+
CONTEXT = 'https://purl.imsglobal.org/spec/lti/claim/context'.freeze
|
10
|
+
RESOURCE_LINK = 'https://purl.imsglobal.org/spec/lti/claim/resource_link'.freeze
|
11
|
+
ROLES = 'https://purl.imsglobal.org/spec/lti/claim/roles'.freeze
|
12
|
+
CUSTOM = 'https://purl.imsglobal.org/spec/lti/claim/custom'.freeze
|
13
|
+
end
|
14
|
+
|
15
|
+
# LTI Deep Linking Claims
|
16
|
+
module DeepLinking
|
17
|
+
CONTENT_ITEMS = 'https://purl.imsglobal.org/spec/lti-dl/claim/content_items'.freeze
|
18
|
+
DATA = 'https://purl.imsglobal.org/spec/lti-dl/claim/data'.freeze
|
19
|
+
DEEP_LINKING_SETTINGS = 'https://purl.imsglobal.org/spec/lti-dl/claim/deep_linking_settings'.freeze
|
20
|
+
end
|
21
|
+
|
22
|
+
# Canvas-specific Claims
|
23
|
+
module Canvas
|
24
|
+
PLACEMENT = 'https://www.instructure.com/placement'.freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
# LTI Message Types
|
28
|
+
module MessageTypes
|
29
|
+
DEEP_LINKING_REQUEST = 'LtiDeepLinkingRequest'.freeze
|
30
|
+
DEEP_LINKING_RESPONSE = 'LtiDeepLinkingResponse'.freeze
|
31
|
+
RESOURCE_LINK_REQUEST = 'LtiResourceLinkRequest'.freeze
|
32
|
+
end
|
33
|
+
|
34
|
+
# LTI Versions
|
35
|
+
module Versions
|
36
|
+
V1_3 = '1.3.0'.freeze
|
37
|
+
end
|
38
|
+
|
39
|
+
# Deep Linking Settings Keys
|
40
|
+
module DeepLinkingSettings
|
41
|
+
DEEP_LINK_RETURN_URL = 'deep_link_return_url'.freeze
|
42
|
+
ACCEPT_TYPES = 'accept_types'.freeze
|
43
|
+
ACCEPT_PRESENTATION_DOCUMENT_TARGETS = 'accept_presentation_document_targets'.freeze
|
44
|
+
ACCEPT_MEDIA_TYPES = 'accept_media_types'.freeze
|
45
|
+
AUTO_CREATE = 'auto_create'.freeze
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/panda_pal/plugins.rb
CHANGED
File without changes
|
data/lib/panda_pal/version.rb
CHANGED
data/lib/panda_pal.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "panda_pal/engine"
|
2
2
|
require 'panda_pal/plugins'
|
3
3
|
require 'panda_pal/helpers'
|
4
|
+
require 'panda_pal/lti_constants'
|
4
5
|
require 'oauth/request_proxy/rack_request'
|
5
6
|
require 'oauth/request_proxy/action_dispatch_request'
|
6
7
|
|
@@ -50,9 +51,22 @@ module PandaPal
|
|
50
51
|
@@lti_custom_params.deep_dup
|
51
52
|
end
|
52
53
|
|
53
|
-
def self.stage_navigation(navigation, options)
|
54
|
-
|
55
|
-
|
54
|
+
def self.stage_navigation(navigation, options = {})
|
55
|
+
warn "[DEPRECATION] `stage_navigation` is deprecated. Please use `stage_placement` instead."
|
56
|
+
stage_placement(navigation, options)
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.stage_placement(placement, options = {})
|
60
|
+
@@lti_navigation[placement] ||= {}
|
61
|
+
|
62
|
+
if options.delete(:deep_linking)
|
63
|
+
options = options.merge({
|
64
|
+
message_type: LtiConstants::MessageTypes::DEEP_LINKING_REQUEST,
|
65
|
+
route_helper_key: :deep_link_request
|
66
|
+
})
|
67
|
+
end
|
68
|
+
|
69
|
+
@@lti_navigation[placement].merge!(options)
|
56
70
|
end
|
57
71
|
|
58
72
|
def self.lti_paths
|
@@ -70,7 +84,7 @@ module PandaPal
|
|
70
84
|
end
|
71
85
|
|
72
86
|
def self.lti_private_key=(v)
|
73
|
-
@@lti_private_key =
|
87
|
+
@@lti_private_key = v
|
74
88
|
end
|
75
89
|
|
76
90
|
def self.register_extension(type, modul)
|
File without changes
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: panda_pal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.
|
4
|
+
version: 5.16.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Instructure CustomDev
|
@@ -159,6 +159,7 @@ files:
|
|
159
159
|
- app/lib/lti_xml/base_platform.rb
|
160
160
|
- app/lib/lti_xml/bridge_platform.rb
|
161
161
|
- app/lib/lti_xml/canvas_platform.rb
|
162
|
+
- app/lib/panda_pal/deep_linking_helpers.rb
|
162
163
|
- app/lib/panda_pal/launch_url_helpers.rb
|
163
164
|
- app/lib/panda_pal/lti_jwt_validator.rb
|
164
165
|
- app/models/panda_pal/api_call.rb
|
@@ -197,6 +198,7 @@ files:
|
|
197
198
|
- lib/panda_pal/helpers/route_helper.rb
|
198
199
|
- lib/panda_pal/helpers/secure_headers.rb
|
199
200
|
- lib/panda_pal/helpers/session_replacement.rb
|
201
|
+
- lib/panda_pal/lti_constants.rb
|
200
202
|
- lib/panda_pal/plugins.rb
|
201
203
|
- lib/panda_pal/spec_helper.rb
|
202
204
|
- lib/panda_pal/version.rb
|