panda_pal 5.6.7.beta1 → 5.6.7
Sign up to get free protection for your applications and to get access to all the features.
- 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:
|