panda_pal 5.6.7.beta1 → 5.6.7
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/app/controllers/panda_pal/lti_v1_p3_controller.rb +4 -6
- data/app/models/panda_pal/organization.rb +6 -28
- data/app/models/panda_pal/organization_concerns/settings_validation.rb +2 -0
- data/app/models/panda_pal/platform/canvas.rb +163 -0
- data/app/models/panda_pal/platform.rb +72 -153
- data/app/models/panda_pal/session.rb +7 -3
- data/lib/panda_pal/helpers/controller_helper.rb +8 -3
- data/lib/panda_pal/helpers/secure_headers.rb +3 -1
- data/lib/panda_pal/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c335f4ab51dec72916a2a630af60dbbed1f681bcd220cbf4585f5291127ca8ac
|
4
|
+
data.tar.gz: 3031b40de1ba6899077609843b1ed57d30cec38867f68f3c9c45281affea54c2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7c225b256e06497ddc5ca6faf9604a28cb9290d0b37911ad39ac00e88beb2d3041541d758e55e992608db79ca9b5249c8aaf0ca69310b651f6635095fc7a2650
|
7
|
+
data.tar.gz: 7d2e7c371bd4bab83ee8c2e001d8affc48d0f5e88b2c21476b181c134a37de9db535c527b41812759eef0d0b96cc7d6bb3a427ef7187f5bc9d3efca145f59838
|
@@ -12,14 +12,11 @@ module PandaPal
|
|
12
12
|
around_action :switch_tenant, only: [:resource_link_request]
|
13
13
|
|
14
14
|
def login
|
15
|
-
|
16
|
-
# May need to move Platform#jwks_url and Platform#authentication_redirect_url to
|
17
|
-
# a new GenericPlatform class/model and then associate the here-generated Session with
|
18
|
-
# the public tenant, re-homing it once we have a deployment_id available
|
19
|
-
# ... or create a new AuthState model instead of using Session here
|
20
|
-
@organization = PandaPal::Organization.find_by!(key: params[:client_id])
|
15
|
+
@current_lti_platform = PandaPal::Platform.resolve_platform(params)
|
21
16
|
|
17
|
+
current_session_data[:lti_platform] = @current_lti_platform&.serialize
|
22
18
|
current_session_data[:lti_oauth_nonce] = SecureRandom.uuid
|
19
|
+
current_session.panda_pal_organization_id = -1
|
23
20
|
|
24
21
|
@form_action = current_lti_platform.authentication_redirect_url
|
25
22
|
@method = :post
|
@@ -39,6 +36,7 @@ module PandaPal
|
|
39
36
|
|
40
37
|
def resource_link_request
|
41
38
|
# Redirect to correct region/env?
|
39
|
+
|
42
40
|
if params[:launch_type]
|
43
41
|
current_session_data.merge!({
|
44
42
|
lti_version: 'v1p3',
|
@@ -88,20 +88,8 @@ module PandaPal
|
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
|
-
def
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
def lti_platform_type
|
96
|
-
platform = PandaPal.lti_options[:platform] || 'canvas.instructure.com'
|
97
|
-
case platform
|
98
|
-
when 'canvas.instructure.com'
|
99
|
-
PandaPal::Platform::Canvas
|
100
|
-
# when 'bridgeapp.com'
|
101
|
-
# TODO Add support for Bridge?
|
102
|
-
else
|
103
|
-
raise "Unknown platform '#{platform}'"
|
104
|
-
end
|
91
|
+
def self.resolve_platform(hint)
|
92
|
+
iss = hint["iss"]
|
105
93
|
end
|
106
94
|
|
107
95
|
def rename!(new_name)
|
@@ -114,25 +102,15 @@ module PandaPal
|
|
114
102
|
switch_tenant if do_switch
|
115
103
|
end
|
116
104
|
|
117
|
-
|
118
|
-
(
|
119
|
-
|
120
|
-
|
121
|
-
def method_missing(method, *args, **kwargs, &block)
|
122
|
-
ext = platform_org_extension_module
|
123
|
-
if (ext.instance_method(method) rescue nil)
|
124
|
-
ext.instance_method(method).bind_call(self, *args, **kwargs, &block)
|
125
|
-
else
|
126
|
-
super
|
105
|
+
if !PandaPal.lti_options[:platform].present? || PandaPal.lti_options[:platform].is_a?(String)
|
106
|
+
platform_cls = Platform.resolve_platform_class(nil) rescue nil
|
107
|
+
if platform_cls && defined?(platform_cls::OrgExtension)
|
108
|
+
include platform_cls::OrgExtension
|
127
109
|
end
|
128
110
|
end
|
129
111
|
|
130
112
|
private
|
131
113
|
|
132
|
-
def platform_org_extension_module
|
133
|
-
defined?(lti_platform_type::OrgExtension) ? lti_platform_type::OrgExtension : nil
|
134
|
-
end
|
135
|
-
|
136
114
|
def create_schema
|
137
115
|
Apartment::Tenant.create name
|
138
116
|
end
|
@@ -0,0 +1,163 @@
|
|
1
|
+
begin
|
2
|
+
require 'bearcat'
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
6
|
+
module PandaPal
|
7
|
+
class Platform::Canvas < Platform
|
8
|
+
attr_accessor :organization
|
9
|
+
|
10
|
+
KNOWN_ISSUERS = [
|
11
|
+
"https://canvas.instructure.com",
|
12
|
+
"https://canvas.beta.instructure.com",
|
13
|
+
"https://canvas.test.instructure.com",
|
14
|
+
]
|
15
|
+
|
16
|
+
def jwks_url
|
17
|
+
"#{@issuer}/api/lti/security/jwks"
|
18
|
+
end
|
19
|
+
|
20
|
+
def authentication_redirect_url
|
21
|
+
"#{@issuer}/api/lti/authorize_redirect"
|
22
|
+
end
|
23
|
+
|
24
|
+
def grant_url
|
25
|
+
"#{@issuer}/login/oauth2/token"
|
26
|
+
end
|
27
|
+
|
28
|
+
module OrgExtension
|
29
|
+
extend ActiveSupport::Concern
|
30
|
+
|
31
|
+
def install_lti(host: nil, context: :root_account, version: 'v1p1', exists: :error)
|
32
|
+
raise "Automatically installing this LTI requires Bearcat." unless defined?(Bearcat)
|
33
|
+
|
34
|
+
context = context.to_s
|
35
|
+
context = 'account/self' if context == 'root_account'
|
36
|
+
cid, ctype = context.split(/[\.\/]/).reverse
|
37
|
+
ctype ||= 'account'
|
38
|
+
|
39
|
+
existing_installs = bearcat_client.send(:"#{ctype}_external_tools", cid).all_pages_each.filter do |cet|
|
40
|
+
cet[:consumer_key] == self.key
|
41
|
+
end
|
42
|
+
|
43
|
+
if existing_installs.present?
|
44
|
+
case exists
|
45
|
+
when :error
|
46
|
+
raise "Tool with key #{self.key} already installed"
|
47
|
+
when :duplicate
|
48
|
+
when :replace
|
49
|
+
existing_installs.each do |install|
|
50
|
+
bearcat_client.send(:"delete_#{ctype}_external_tool", cid, install[:id])
|
51
|
+
end
|
52
|
+
else
|
53
|
+
raise "exists: #{exists} is not supported"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# TODO LTI 1.3 Support
|
58
|
+
|
59
|
+
conf = {
|
60
|
+
name: PandaPal.lti_options[:title],
|
61
|
+
description: PandaPal.lti_options[:description],
|
62
|
+
consumer_key: self.key,
|
63
|
+
shared_secret: self.secret,
|
64
|
+
privacy_level: "public",
|
65
|
+
config_type: 'by_url',
|
66
|
+
config_url: PandaPal::LaunchUrlHelpers.resolve_route(:v1p0_config_url, host: host),
|
67
|
+
}
|
68
|
+
|
69
|
+
bearcat_client.send(:"create_#{ctype}_external_tool", cid, conf)
|
70
|
+
end
|
71
|
+
|
72
|
+
def reinstall_lti!(*args, **kwargs)
|
73
|
+
install_lti(*args, exists: :replace, **kwargs)
|
74
|
+
end
|
75
|
+
|
76
|
+
def lti_api_configuration(host: nil)
|
77
|
+
PandaPal::LaunchUrlHelpers.with_uri_host(host) do
|
78
|
+
domain = PandaPal.lti_properties[:domain] || host.host
|
79
|
+
launch_url = PandaPal.lti_options[:secure_launch_url] ||
|
80
|
+
"#{domain}#{PandaPal.lti_options[:secure_launch_path]}" ||
|
81
|
+
PandaPal.lti_options[:launch_url] ||
|
82
|
+
"#{domain}#{PandaPal.lti_options[:launch_path]}" ||
|
83
|
+
domain
|
84
|
+
|
85
|
+
lti_json = {
|
86
|
+
name: PandaPal.lti_options[:title],
|
87
|
+
description: PandaPal.lti_options[:description],
|
88
|
+
domain: host.host,
|
89
|
+
url: launch_url,
|
90
|
+
consumer_key: self.key,
|
91
|
+
shared_secret: self.secret,
|
92
|
+
privacy_level: "public",
|
93
|
+
|
94
|
+
custom_fields: {},
|
95
|
+
|
96
|
+
environments: PandaPal.lti_environments,
|
97
|
+
}
|
98
|
+
|
99
|
+
lti_json = lti_json.with_indifferent_access
|
100
|
+
lti_json.merge!(PandaPal.lti_properties)
|
101
|
+
|
102
|
+
(PandaPal.lti_options[:custom_fields] || []).each do |k, v|
|
103
|
+
lti_json[:custom_fields][k] = v
|
104
|
+
end
|
105
|
+
|
106
|
+
PandaPal.lti_paths.each do |k, options|
|
107
|
+
options = PandaPal::LaunchUrlHelpers.normalize_lti_launch_desc(options)
|
108
|
+
options[:url] = PandaPal::LaunchUrlHelpers.absolute_launch_url(
|
109
|
+
k.to_sym,
|
110
|
+
host: host,
|
111
|
+
launch_handler: :v1p0_launch_path,
|
112
|
+
default_auto_launch: false
|
113
|
+
).to_s
|
114
|
+
lti_json[k] = options
|
115
|
+
end
|
116
|
+
|
117
|
+
lti_json
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def canvas_url
|
122
|
+
PandaPal::Platform.find_org_setting([
|
123
|
+
"canvas.base_url",
|
124
|
+
"canvas_url",
|
125
|
+
"canvas_base_url",
|
126
|
+
"canvas.url",
|
127
|
+
"base_url",
|
128
|
+
], self) || (Rails.env.development? && 'http://localhost:3000') || 'https://canvas.instructure.com'
|
129
|
+
end
|
130
|
+
|
131
|
+
def canvas_api_token
|
132
|
+
PandaPal::Platform.find_org_setting([
|
133
|
+
"canvas.api_token",
|
134
|
+
"canvas.api_key",
|
135
|
+
"canvas.token",
|
136
|
+
"canvas_api_token",
|
137
|
+
"canvas_token",
|
138
|
+
"api_token",
|
139
|
+
], self)
|
140
|
+
end
|
141
|
+
|
142
|
+
def root_account_info
|
143
|
+
Rails.cache.fetch("panda_pal/org:#{name}/root_account_info", expires_in: 24.hours) do
|
144
|
+
response = bearcat_client.account("self")
|
145
|
+
response = bearcat_client.account(response[:root_account_id]) if response[:root_account_id].present?
|
146
|
+
response
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
if defined?(Bearcat)
|
151
|
+
def bearcat_client
|
152
|
+
return canvas_sync_client if defined?(canvas_sync_client)
|
153
|
+
|
154
|
+
Bearcat::Client.new(
|
155
|
+
prefix: canvas_url,
|
156
|
+
token: canvas_token,
|
157
|
+
master_rate_limit: (Rails.env.production? && !!defined?(Redis) && ENV['REDIS_URL'].present?),
|
158
|
+
)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
@@ -1,187 +1,106 @@
|
|
1
|
-
begin
|
2
|
-
require 'bearcat'
|
3
|
-
rescue LoadError
|
4
|
-
end
|
5
1
|
|
6
2
|
require 'httparty'
|
7
3
|
|
8
4
|
module PandaPal
|
9
5
|
class Platform
|
6
|
+
require_relative "platform/canvas"
|
7
|
+
|
8
|
+
def initialize(params)
|
9
|
+
@issuer = params[:iss]
|
10
|
+
end
|
11
|
+
|
10
12
|
def public_jwks
|
11
13
|
require "json/jwt"
|
12
14
|
|
13
|
-
|
14
|
-
|
15
|
+
jwk_json = Rails.cache.fetch("panda_pal/jwks/#{jwks_url}")
|
16
|
+
jwk_json ||= begin
|
17
|
+
response = HTTParty.get(jwks_url)
|
18
|
+
response.success? ? response.body : nil
|
19
|
+
end
|
15
20
|
|
16
|
-
|
17
|
-
rescue
|
18
|
-
nil
|
19
|
-
end
|
21
|
+
return nil unless jwk_json.present?
|
20
22
|
|
21
|
-
|
23
|
+
Rails.cache.write("panda_pal/jwks/#{jwks_url}", jwk_json, expires_in: 24.hours)
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
-
p = p.split('.').map(&:to_sym)
|
26
|
-
val = org.settings.dig(*p)
|
27
|
-
return val if val.present?
|
28
|
-
end
|
25
|
+
JSON::JWK::Set.new(JSON.parse(jwk_json))
|
26
|
+
rescue
|
29
27
|
nil
|
30
28
|
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class Platform::Canvas < Platform
|
34
|
-
attr_accessor :organization
|
35
29
|
|
36
|
-
def
|
37
|
-
|
30
|
+
def self.deserialize(params)
|
31
|
+
params[:platform_class].safe_constantize.new(params)
|
38
32
|
end
|
39
33
|
|
40
|
-
def
|
41
|
-
|
34
|
+
def serialize
|
35
|
+
{
|
36
|
+
platform_class: self.class.to_s,
|
37
|
+
iss: @issuer,
|
38
|
+
}
|
42
39
|
end
|
43
40
|
|
44
|
-
def
|
45
|
-
|
41
|
+
def self.resolve_platform(params)
|
42
|
+
resolved = resolve_raw_platform(params)
|
43
|
+
resolved.is_a?(Class) ? resolved.new({ iss: params[:iss] }) : resolved
|
46
44
|
end
|
47
45
|
|
48
|
-
def
|
49
|
-
|
46
|
+
def self.resolve_platform_class(params)
|
47
|
+
resolved = resolve_raw_platform(params)
|
48
|
+
resolved.is_a?(Class) ? resolved : resolved.class
|
50
49
|
end
|
51
50
|
|
52
|
-
def
|
53
|
-
"
|
54
|
-
end
|
51
|
+
def self.resolve_raw_platform(params)
|
52
|
+
platform = PandaPal.lti_options[:platform] || "canvas.instructure.com"
|
55
53
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
def install_lti(host: nil, context: :root_account, version: 'v1p1', exists: :error)
|
60
|
-
raise "Automatically installing this LTI requires Bearcat." unless defined?(Bearcat)
|
61
|
-
|
62
|
-
context = context.to_s
|
63
|
-
context = 'account/self' if context == 'root_account'
|
64
|
-
cid, ctype = context.split(/[\.\/]/).reverse
|
65
|
-
ctype ||= 'account'
|
66
|
-
|
67
|
-
existing_installs = bearcat_client.send(:"#{ctype}_external_tools", cid).all_pages_each.filter do |cet|
|
68
|
-
cet[:consumer_key] == self.key
|
69
|
-
end
|
70
|
-
|
71
|
-
if existing_installs.present?
|
72
|
-
case exists
|
73
|
-
when :error
|
74
|
-
raise "Tool with key #{self.key} already installed"
|
75
|
-
when :duplicate
|
76
|
-
when :replace
|
77
|
-
existing_installs.each do |install|
|
78
|
-
bearcat_client.send(:"delete_#{ctype}_external_tool", cid, install[:id])
|
79
|
-
end
|
80
|
-
else
|
81
|
-
raise "exists: #{exists} is not supported"
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# TODO LTI 1.3 Support
|
86
|
-
|
87
|
-
conf = {
|
88
|
-
name: PandaPal.lti_options[:title],
|
89
|
-
description: PandaPal.lti_options[:description],
|
90
|
-
consumer_key: self.key,
|
91
|
-
shared_secret: self.secret,
|
92
|
-
privacy_level: "public",
|
93
|
-
config_type: 'by_url',
|
94
|
-
config_url: PandaPal::LaunchUrlHelpers.resolve_route(:v1p0_config_url, host: host),
|
95
|
-
}
|
96
|
-
|
97
|
-
bearcat_client.send(:"create_#{ctype}_external_tool", cid, conf)
|
98
|
-
end
|
54
|
+
if platform.is_a? Symbol
|
55
|
+
platform_const = Object.send(platform, params)
|
56
|
+
return platform_const if platform_const
|
99
57
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
launch_url = PandaPal.lti_options[:secure_launch_url] ||
|
104
|
-
"#{domain}#{PandaPal.lti_options[:secure_launch_path]}" ||
|
105
|
-
PandaPal.lti_options[:launch_url] ||
|
106
|
-
"#{domain}#{PandaPal.lti_options[:launch_path]}" ||
|
107
|
-
domain
|
108
|
-
|
109
|
-
lti_json = {
|
110
|
-
name: PandaPal.lti_options[:title],
|
111
|
-
description: PandaPal.lti_options[:description],
|
112
|
-
domain: host.host,
|
113
|
-
url: launch_url,
|
114
|
-
consumer_key: self.key,
|
115
|
-
shared_secret: self.secret,
|
116
|
-
privacy_level: "public",
|
117
|
-
|
118
|
-
custom_fields: {},
|
119
|
-
|
120
|
-
environments: PandaPal.lti_environments,
|
121
|
-
}
|
122
|
-
|
123
|
-
lti_json = lti_json.with_indifferent_access
|
124
|
-
lti_json.merge!(PandaPal.lti_properties)
|
125
|
-
|
126
|
-
(PandaPal.lti_options[:custom_fields] || []).each do |k, v|
|
127
|
-
lti_json[:custom_fields][k] = v
|
128
|
-
end
|
129
|
-
|
130
|
-
PandaPal.lti_paths.each do |k, options|
|
131
|
-
options = PandaPal::LaunchUrlHelpers.normalize_lti_launch_desc(options)
|
132
|
-
options[:url] = PandaPal::LaunchUrlHelpers.absolute_launch_url(
|
133
|
-
k.to_sym,
|
134
|
-
host: host,
|
135
|
-
launch_handler: :v1p0_launch_path,
|
136
|
-
default_auto_launch: false
|
137
|
-
).to_s
|
138
|
-
lti_json[k] = options
|
139
|
-
end
|
140
|
-
|
141
|
-
lti_json
|
142
|
-
end
|
143
|
-
end
|
58
|
+
elsif platform.is_a? Proc
|
59
|
+
platform_const = platform.call(params)
|
60
|
+
return platform_const if platform_const
|
144
61
|
|
145
|
-
|
146
|
-
|
147
|
-
"canvas.base_url",
|
148
|
-
"canvas_url",
|
149
|
-
"canvas_base_url",
|
150
|
-
"canvas.url",
|
151
|
-
"base_url",
|
152
|
-
], self) || (Rails.env.development? && 'http://localhost:3000') || 'https://canvas.instructure.com'
|
153
|
-
end
|
62
|
+
else
|
63
|
+
return Platform::Canvas if platform == "canvas.instructure.com"
|
154
64
|
|
155
|
-
|
156
|
-
|
157
|
-
"canvas.api_token",
|
158
|
-
"canvas.api_key",
|
159
|
-
"canvas.token",
|
160
|
-
"canvas_api_token",
|
161
|
-
"canvas_token",
|
162
|
-
"api_token",
|
163
|
-
], self)
|
65
|
+
platform_const = platform.safe_constantize
|
66
|
+
return platform_const if platform_const.is_a? Class
|
164
67
|
end
|
165
68
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
end
|
172
|
-
end
|
69
|
+
# TODO Default logic?
|
70
|
+
return Platform::Canvas if Platform::Canvas::KNOWN_ISSUERS.include?(params[:iss])
|
71
|
+
|
72
|
+
raise "Unknown platform '#{platform}'"
|
73
|
+
end
|
173
74
|
|
174
|
-
|
175
|
-
|
176
|
-
|
75
|
+
def self.materialize_extension(org)
|
76
|
+
return nil unless defined?(self::OrgExtension)
|
77
|
+
|
78
|
+
ext_mod = self::OrgExtension
|
79
|
+
org = org.dup
|
80
|
+
org.extend(ext_mod)
|
81
|
+
|
82
|
+
org
|
83
|
+
end
|
84
|
+
|
85
|
+
protected
|
177
86
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
end
|
87
|
+
def self.find_org_setting(paths, org = current_organization)
|
88
|
+
paths.each do |p|
|
89
|
+
p = p.split('.').map(&:to_sym)
|
90
|
+
val = org.settings.dig(*p)
|
91
|
+
return val if val.present?
|
184
92
|
end
|
93
|
+
nil
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Platform::Generic < Platform
|
98
|
+
attr_reader :jwks_url, :authentication_redirect_url, :grant_url
|
99
|
+
|
100
|
+
def init(base_url, jwks: "/api/lti/security/jwks", auth_redirect: "/api/lti/authorize_redirect", grant: "/login/oauth2/token")
|
101
|
+
@jwks_url = URI.join(base_url, jwks).to_s
|
102
|
+
@authentication_redirect_url = URI.join(base_url, auth_redirect).to_s
|
103
|
+
@grant_url = URI.join(base_url, grant).to_s
|
185
104
|
end
|
186
105
|
end
|
187
106
|
end
|
@@ -1,13 +1,17 @@
|
|
1
1
|
module PandaPal
|
2
2
|
class Session < ActiveRecord::Base
|
3
|
-
belongs_to :panda_pal_organization, class_name: 'PandaPal::Organization'
|
4
|
-
|
5
|
-
validates :panda_pal_organization_id, presence: true
|
3
|
+
belongs_to :panda_pal_organization, class_name: 'PandaPal::Organization', optional: true
|
6
4
|
|
7
5
|
after_initialize do
|
8
6
|
self.session_key ||= SecureRandom.urlsafe_base64(60)
|
9
7
|
end
|
10
8
|
|
9
|
+
def lti_platform
|
10
|
+
return nil unless data[:lti_platform].present?
|
11
|
+
|
12
|
+
@lti_platform ||= Platform.deserialize(data[:lti_platform])
|
13
|
+
end
|
14
|
+
|
11
15
|
class DataSerializer
|
12
16
|
def self.load(str)
|
13
17
|
return {} unless str.present?
|
@@ -13,7 +13,7 @@ module PandaPal::Helpers
|
|
13
13
|
end
|
14
14
|
|
15
15
|
def current_lti_platform
|
16
|
-
@current_lti_platform ||=
|
16
|
+
@current_lti_platform ||= current_session(create_missing: false)&.lti_platform
|
17
17
|
end
|
18
18
|
|
19
19
|
def lti_launch_params
|
@@ -55,6 +55,8 @@ module PandaPal::Helpers
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def validate_v1p3_launch
|
58
|
+
require "json/jwt"
|
59
|
+
|
58
60
|
decoded_jwt = JSON::JWT.decode(params.require(:id_token), :skip_verification)
|
59
61
|
raise JSON::JWT::VerificationFailed, 'error decoding id_token' if decoded_jwt.blank?
|
60
62
|
|
@@ -70,14 +72,17 @@ module PandaPal::Helpers
|
|
70
72
|
|
71
73
|
raise JSON::JWT::VerificationFailed, 'Unrecognized Organization' unless @organization.present?
|
72
74
|
|
75
|
+
params[:session_key] = params[:state]
|
76
|
+
|
73
77
|
decoded_jwt.verify!(current_lti_platform.public_jwks)
|
74
78
|
|
75
|
-
params[:session_key] = params[:state]
|
76
79
|
raise JSON::JWT::VerificationFailed, 'State is invalid' unless current_session_data[:lti_oauth_nonce] == decoded_jwt['nonce']
|
77
80
|
|
78
81
|
jwt_verifier = PandaPal::LtiJwtValidator.new(decoded_jwt, client_id)
|
79
82
|
raise JSON::JWT::VerificationFailed, jwt_verifier.errors unless jwt_verifier.valid?
|
80
83
|
|
84
|
+
current_session.update(panda_pal_organization: @organization)
|
85
|
+
|
81
86
|
@decoded_lti_jwt = decoded_jwt
|
82
87
|
rescue JSON::JWT::VerificationFailed => e
|
83
88
|
payload = Array(e.message)
|
@@ -124,7 +129,7 @@ module PandaPal::Helpers
|
|
124
129
|
|
125
130
|
def find_or_create_session(key:)
|
126
131
|
if key == :create
|
127
|
-
PandaPal::Session.new(panda_pal_organization_id: current_organization
|
132
|
+
PandaPal::Session.new(panda_pal_organization_id: current_organization&.id)
|
128
133
|
else
|
129
134
|
PandaPal::Session.find_by(session_key: key)
|
130
135
|
end
|
@@ -29,9 +29,11 @@ module PandaPal
|
|
29
29
|
|
30
30
|
# Allow stuff like rack-mini-profiler to work in development:
|
31
31
|
# https://github.com/MiniProfiler/rack-mini-profiler/issues/327
|
32
|
-
# DON'T ENABLE THIS FOR PRODUCTION!
|
33
32
|
csp_entry(:script_src, "'unsafe-eval'")
|
34
33
|
|
34
|
+
# React Devtools
|
35
|
+
csp_entry(:script_src, "'unsafe-inline'")
|
36
|
+
|
35
37
|
# Detect and permit Scout APM in Dev
|
36
38
|
if MiscHelper.to_boolean(ENV['SCOUT_DEV_TRACE'])
|
37
39
|
csp_entry(:default_src, 'https://scoutapm.com')
|
data/lib/panda_pal/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: panda_pal
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 5.6.7
|
4
|
+
version: 5.6.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Instructure ProServe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -315,6 +315,7 @@ files:
|
|
315
315
|
- app/models/panda_pal/organization_concerns/settings_validation.rb
|
316
316
|
- app/models/panda_pal/organization_concerns/task_scheduling.rb
|
317
317
|
- app/models/panda_pal/platform.rb
|
318
|
+
- app/models/panda_pal/platform/canvas.rb
|
318
319
|
- app/models/panda_pal/session.rb
|
319
320
|
- app/views/layouts/panda_pal/application.html.erb
|
320
321
|
- app/views/panda_pal/lti/launch.html.erb
|
@@ -402,9 +403,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
402
403
|
version: '0'
|
403
404
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
404
405
|
requirements:
|
405
|
-
- - "
|
406
|
+
- - ">="
|
406
407
|
- !ruby/object:Gem::Version
|
407
|
-
version:
|
408
|
+
version: '0'
|
408
409
|
requirements: []
|
409
410
|
rubygems_version: 3.1.6
|
410
411
|
signing_key:
|