panda_pal 5.3.6.beta2 → 5.3.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1797969aa547664019c9091a2286fc7ed2958e749c392094db1818e3f2826984
4
- data.tar.gz: 53049f9345f939d99f7083dbf49cd3a96f15f3282252a1b4a518aa16d589632d
3
+ metadata.gz: 42e9ffbf3704935100156e28b5d3f2490fba75a5974b352f0092c14bb8b87ffb
4
+ data.tar.gz: 5d511a85fe356dfe659ecaacfc7023315f649947ff483c6969e253802549e825
5
5
  SHA512:
6
- metadata.gz: 5088fcf2d122b23da5a2f82ac3054d64fe6974c6c984d9c3726db910bb101b67816b92b8a4b43b47e818fbb85f3bd02a9487d0ca2fd322daeea7cc475bf67b41
7
- data.tar.gz: 16d8ee8931f7911a00486f7c5fe0774a4cc80bc942b3477458df0e3bcb0dcc274f519789f10fb1b6e6c4d28419ef45649460c2c09104b95b81cf96c6478fe1bc
6
+ metadata.gz: a4d2a7242e203c4c86866ff7efb3ba211c2b3b7f41463b776785371e06997470fdee569ed4e4ba0025a7eef6198bb067e1fdf71fc8b7c9307bf26c1d343de7db
7
+ data.tar.gz: 7c3f0facf0767b7afea02ad0368bf6a0d79c8522109289e463a95719cd7f3762a34b5ad641499ff96871815f2db1ec76f192dd6221936d3af2586399a402bf1f
data/README.md CHANGED
@@ -26,7 +26,8 @@ LTI 1.3 has some additional configuration steps required to setup an LTI:
26
26
 
27
27
  1. If you're running Canvas locally, make sure the `config/redis.yml` and `config/dynamic_settings.yml` files exist in Canvas.
28
28
  2. In prod, you'll need to generate a RSA Private Key for the LTI to use. You can set the `LTI_PRIVATE_KEY` ENV variable, or manually set `PandaPal.lti_private_key = OpenSSL::PKey::RSA.new(key)`.
29
- 3. Your PandaPal::Organization's `key` should be `CLIENT_ID/DEPLOYMENT_ID` (which can be found in Canvas). If a Deployment ID is not given, the key should just be `CLIENT_ID/`.
29
+ 3. Make sure you have Redis installed and linked correctly
30
+ 4. Your PandaPal::Organization's `key` should be `CLIENT_ID/DEPLOYMENT_ID` (which can be found in Canvas). If a Deployment ID is not given, the key should just be `CLIENT_ID`.
30
31
 
31
32
  ### Launch URL property
32
33
  LTI Spec: `The launch_url contains the URL to which the LTI Launch is to be sent. The secure_launch_url is the URL to use if secure http is required. One of either the launch_url or the secure_launch_url must be specified.`
@@ -92,10 +93,22 @@ The following routes should be added to the routes.rb file of the implementing L
92
93
  ```ruby
93
94
  # config/routes.rb
94
95
  mount PandaPal::Engine, at: '/lti'
95
- lti_nav account_navigation: 'accounts#launch' # Use lti_nav to provide a custom Launch implementation, otherwise use the url: param of stage_navigation to let PandaPal handle launch.
96
96
  root to: 'panda_pal/lti#launch'
97
+
98
+ # Add Launch Endpoints:
99
+ lti_nav account_navigation: 'accounts#launch', auto_launch: false # (LTI <1.3 Default)
100
+ # -- OR --
101
+ scope '/organizations/:organization_id' do
102
+ lti_nav account_navigation: 'accounts#launch_landing', auto_launch: true # (LTI 1.3 Default)
103
+ lti_nav account_navigation: 'accounts#launch_landing' # Automatically sets auto_launch to true because :organization_id is part of the path
104
+ # ...
105
+ end
97
106
  ```
98
107
 
108
+ `auto_launch`: Setting to `true` will tell PandaPal to handle all of the launch details and session creation, and then pass off to
109
+ the defined action. Setting it to `false` indicates that the defined action handles launch validation and setup itself (this has been the legacy approach).
110
+ 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`
111
+
99
112
  ## Implementating data segregation
100
113
  This engine uses Apartment to keep data segregated between installations of the implementing LTI tool.
101
114
  By default, it does this by inspecting the path of the request, and matching URLs containing `orgs` or `organizations`,
@@ -2,7 +2,7 @@ require_dependency "panda_pal/application_controller"
2
2
 
3
3
  module PandaPal
4
4
  class LtiV1P0Controller < ApplicationController
5
- skip_forgery_protection
5
+ skip_before_action :verify_authenticity_token
6
6
 
7
7
  def launch
8
8
  current_session_data.merge!({
@@ -2,7 +2,7 @@ require_dependency "panda_pal/application_controller"
2
2
 
3
3
  module PandaPal
4
4
  class LtiV1P3Controller < ApplicationController
5
- skip_forgery_protection
5
+ skip_before_action :verify_authenticity_token
6
6
  before_action :validate_launch!, only: [:resource_link_request]
7
7
  around_action :switch_tenant, only: [:resource_link_request]
8
8
 
@@ -55,11 +55,15 @@ module PandaPal
55
55
  parsed_request_url = URI.parse(request_url)
56
56
 
57
57
  mapped_placements = PandaPal.lti_paths.map do |k, opts|
58
- opts = opts.dup
59
- opts.delete(:route_helper_key)
58
+ opts = LaunchUrlHelpers.normalize_lti_launch_desc(opts)
60
59
  opts.merge!({
61
60
  placement: k,
62
- target_link_uri: LaunchUrlHelpers.absolute_launch_url(k.to_sym, host: parsed_request_url, launch_handler: v1p3_resource_link_request_path),
61
+ target_link_uri: LaunchUrlHelpers.absolute_launch_url(
62
+ k.to_sym,
63
+ host: parsed_request_url,
64
+ launch_handler: v1p3_resource_link_request_path,
65
+ default_auto_launch: true
66
+ ),
63
67
  })
64
68
  opts
65
69
  end
@@ -85,8 +85,13 @@ module LtiXml
85
85
  end
86
86
 
87
87
  def ext_params(options, k)
88
- options.delete(:route_helper_key)
89
- options[:url] = PandaPal::LaunchUrlHelpers.absolute_launch_url(k.to_sym, host: parsed_request_url, launch_handler: nil)
88
+ options = PandaPal::LaunchUrlHelpers.normalize_lti_launch_desc(options)
89
+ options[:url] = PandaPal::LaunchUrlHelpers.absolute_launch_url(
90
+ k.to_sym,
91
+ host: parsed_request_url,
92
+ launch_handler: :v1p0_launch_path,
93
+ default_auto_launch: false
94
+ )
90
95
  options
91
96
  end
92
97
  end
@@ -1,22 +1,29 @@
1
1
  module PandaPal
2
2
  module LaunchUrlHelpers
3
- def self.absolute_launch_url(launch_type, host:, launch_handler: nil)
3
+ def self.absolute_launch_url(launch_type, host:, launch_handler: nil, default_auto_launch: false)
4
4
  opts = PandaPal.lti_paths[launch_type]
5
- final_url = launch_url(opts, launch_type: launch_type)
5
+ auto_launch = opts[:auto_launch] != nil ? opts[:auto_launch] : default_auto_launch
6
+ auto_launch = auto_launch && launch_handler.present?
6
7
 
7
- is_direct = opts[:route_helper_key].present? || !launch_handler.present?
8
-
9
- if is_direct
10
- return final_url if URI.parse(final_url).absolute?
11
- return [host.to_s, final_url].join
12
- else
8
+ if auto_launch
13
9
  launch_handler = resolve_route(launch_handler) if launch_handler.is_a?(Symbol)
14
10
  return add_url_params([host.to_s, launch_handler].join, {
15
11
  launch_type: launch_type,
16
12
  })
13
+ else
14
+ final_url = launch_url(opts, launch_type: launch_type)
15
+ return final_url if URI.parse(final_url).absolute?
16
+ return [host.to_s, final_url].join
17
17
  end
18
18
  end
19
19
 
20
+ def self.normalize_lti_launch_desc(opts)
21
+ opts = opts.dup
22
+ opts.delete(:route_helper_key)
23
+ opts.delete(:auto_launch)
24
+ opts
25
+ end
26
+
20
27
  def self.launch_url(opts, launch_type: nil)
21
28
  url = launch_route(opts, launch_type: launch_type)
22
29
  url = resolve_url_symbol(url) if url.is_a?(Symbol)
@@ -99,6 +99,20 @@ module PandaPal
99
99
  errors.concat(val_errors)
100
100
  end
101
101
 
102
+ if settings.is_a?(Array)
103
+ if spec[:length].is_a?(Range)
104
+ errors << "#{human_path} should contain #{spec[:length]} items" unless spec[:length].include?(settings.count)
105
+ elsif spec[:length].is_a?(Numeric)
106
+ errors << "#{human_path} should contain exactly #{spec[:length]} items" unless spec[:length] == settings.count
107
+ end
108
+
109
+ if spec[:item] != nil
110
+ settings.each_with_index do |value, i|
111
+ validate_settings_level(settings[i], spec[:item], path: [*path, i], errors: errors)
112
+ end
113
+ end
114
+ end
115
+
102
116
  if settings.is_a?(Hash)
103
117
  if spec[:properties] != nil
104
118
  spec[:properties].each do |key, pspec|
@@ -10,7 +10,9 @@ Apartment.configure do |config|
10
10
  end
11
11
 
12
12
  Rails.application.config.middleware.use Apartment::Elevators::Generic, lambda { |request|
13
- if match = request.path.match(/\/(?:orgs|organizations)\/(\d+)/)
13
+ if request.path.starts_with?('/rails/active_storage/blobs/')
14
+ PandaPal::Organization.find_by(id: request.params['organization_id']).try(:name)
15
+ elsif match = request.path.match(/\/(?:orgs?|organizations?)\/(\d+)/)
14
16
  PandaPal::Organization.find_by(id: match[1]).try(:name)
15
17
  end
16
18
  }
data/config/routes.rb CHANGED
@@ -3,6 +3,7 @@ PandaPal::Engine.routes.draw do
3
3
 
4
4
  scope '/v1p0', as: 'v1p0' do
5
5
  get '/config' => 'lti_v1_p0#tool_config'
6
+ post '/launch' => 'lti_v1_p0#launch'
6
7
  end
7
8
 
8
9
  scope '/v1p3', as: 'v1p3' do
data/lib/panda_pal.rb CHANGED
@@ -81,6 +81,7 @@ module PandaPal
81
81
 
82
82
  def self.validate_lti_navigation(errors = [])
83
83
  @@lti_navigation.each do |k, v|
84
+ next if v[:route_helper_key]
84
85
  errors << "lti navigation '#{k}' does not have a Route!" unless (LaunchUrlHelpers.launch_url(k) rescue nil)
85
86
  end
86
87
  errors
@@ -39,7 +39,11 @@ module PandaPal::Helpers
39
39
 
40
40
  def validate_v1p0_launch
41
41
  authorized = false
42
- if @organization = params['oauth_consumer_key'] && PandaPal::Organization.find_by_key(params['oauth_consumer_key'])
42
+ # We should verify the timestamp is recent (within 5 minutes). The approved timestamp is part of the signature,
43
+ # so we don't need to worry about malicious users messing with it. We should deny requests that come too long
44
+ # after the approved timestamp.
45
+ good_timestamp = params['oauth_timestamp'] && params['oauth_timestamp'].to_i > Time.now.to_i - 300
46
+ if @organization = good_timestamp && params['oauth_consumer_key'] && PandaPal::Organization.find_by_key(params['oauth_consumer_key'])
43
47
  sanitized_params = request.request_parameters
44
48
  # These params come over with a safari-workaround launch. The authenticator doesn't like them, so clean them out.
45
49
  safe_unexpected_params = ["full_win_launch_requested", "platform_redirect_url", "dummy_param"]
@@ -9,6 +9,12 @@ module PandaPal::Helpers::RouteHelper
9
9
  path = "#{base_path}/#{nav.to_s}"
10
10
 
11
11
  lti_options = options.delete(:lti_options) || {}
12
+ lti_options[:auto_launch] = options.delete(:auto_launch)
13
+
14
+ if lti_options[:auto_launch].nil?
15
+ lti_options[:auto_launch] = (@scope[:path] || '').include?(':organization_id')
16
+ end
17
+
12
18
  lti_options[:route_helper_key] = path.split('/').reject(&:empty?).join('_')
13
19
  post(path, options.dup, &block)
14
20
  get(path, options.dup, &block)
@@ -1,3 +1,3 @@
1
1
  module PandaPal
2
- VERSION = "5.3.6.beta2"
2
+ VERSION = "5.3.10"
3
3
  end
data/panda_pal.gemspec CHANGED
@@ -23,6 +23,7 @@ Gem::Specification.new do |s|
23
23
  s.add_dependency 'attr_encrypted', '~> 3.0.0'
24
24
  s.add_dependency 'secure_headers', '~> 6.1'
25
25
  s.add_dependency 'json-jwt'
26
+ s.add_dependency 'httparty'
26
27
 
27
28
  s.add_development_dependency 'sidekiq'
28
29
  s.add_development_dependency 'sidekiq-scheduler'
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.3.6.beta2
4
+ version: 5.3.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Instructure ProServe
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-12-18 00:00:00.000000000 Z
11
+ date: 2021-02-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -128,6 +128,20 @@ dependencies:
128
128
  - - ">="
129
129
  - !ruby/object:Gem::Version
130
130
  version: '0'
131
+ - !ruby/object:Gem::Dependency
132
+ name: httparty
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ type: :runtime
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
131
145
  - !ruby/object:Gem::Dependency
132
146
  name: sidekiq
133
147
  requirement: !ruby/object:Gem::Requirement
@@ -298,9 +312,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
298
312
  version: '0'
299
313
  required_rubygems_version: !ruby/object:Gem::Requirement
300
314
  requirements:
301
- - - ">"
315
+ - - ">="
302
316
  - !ruby/object:Gem::Version
303
- version: 1.3.1
317
+ version: '0'
304
318
  requirements: []
305
319
  rubygems_version: 3.0.3
306
320
  signing_key: