coalescing_panda 4.7.0 → 5.0.1
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/coalescing_panda/oauth2_controller.rb +2 -0
- data/app/models/coalescing_panda/persistent_session.rb +38 -0
- data/db/migrate/20200528224505_create_coalescing_panda_persistent_session.rb +13 -0
- data/lib/coalescing_panda/controller_helpers.rb +85 -46
- data/lib/coalescing_panda/engine.rb +7 -0
- data/lib/coalescing_panda/version.rb +1 -1
- data/spec/dummy/db/schema.rb +11 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4946cde6027861484869379aa63fb8c0016d413d
|
4
|
+
data.tar.gz: 40794a3d7beab2e690e38da0d0433b0195ab2c60
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0672ab922b73f33f81534f586e918f8c82dec08c14f30e56d1f97d62c0597416529f8f96baab489bf3d9c6c7a114563ff513ae3fa7afd47a15f9faddac2e5695
|
7
|
+
data.tar.gz: 99afede25863235a5db2dfaf59e2ef7d64bf7361243fead2ddb5dce5e0b01db9b73c74c039e65c01afd21956caf0f07abda7ac106ecf9a37b8e782c7113d1667
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module CoalescingPanda
|
2
|
+
class PersistentSession < ActiveRecord::Base
|
3
|
+
serialize :data, Hash
|
4
|
+
belongs_to :coalescing_panda_lti_account, :class_name => 'CoalescingPanda::LtiAccount'
|
5
|
+
validates :coalescing_panda_lti_account_id, presence: true
|
6
|
+
|
7
|
+
after_initialize do
|
8
|
+
self.session_key ||= SecureRandom.urlsafe_base64(60)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.create_from_launch(launch_params, account_id)
|
12
|
+
session = PersistentSession.new(coalescing_panda_lti_account_id: account_id)
|
13
|
+
session.data[:launch_params] = launch_params.to_unsafe_h.with_indifferent_access
|
14
|
+
session.data[:roles] = launch_params['roles'].split(',').map { |role|
|
15
|
+
case role.downcase.strip
|
16
|
+
when 'admin'
|
17
|
+
:admin
|
18
|
+
when 'urn:lti:instrole:ims/lis/administrator'
|
19
|
+
:admin
|
20
|
+
when 'learner'
|
21
|
+
:student
|
22
|
+
when 'instructor'
|
23
|
+
:teacher
|
24
|
+
when 'urn:lti:role:ims/lis/teachingassistant'
|
25
|
+
:ta
|
26
|
+
when 'contentdeveloper'
|
27
|
+
:designer
|
28
|
+
when 'urn:lti:instrole:ims/lis/observer'
|
29
|
+
:observer
|
30
|
+
else
|
31
|
+
:none
|
32
|
+
end
|
33
|
+
}.uniq
|
34
|
+
session.save!
|
35
|
+
session
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateCoalescingPandaPersistentSession < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :coalescing_panda_persistent_sessions do |t|
|
4
|
+
t.string :session_key
|
5
|
+
t.text :data
|
6
|
+
t.integer :coalescing_panda_lti_account_id
|
7
|
+
|
8
|
+
t.timestamps null: false
|
9
|
+
end
|
10
|
+
add_index :coalescing_panda_persistent_sessions, :session_key, unique: true
|
11
|
+
add_index :coalescing_panda_persistent_sessions, :coalescing_panda_lti_account_id, name: 'index_persistent_session_on_lti_account_id'
|
12
|
+
end
|
13
|
+
end
|
@@ -2,6 +2,33 @@ require 'browser'
|
|
2
2
|
|
3
3
|
module CoalescingPanda
|
4
4
|
module ControllerHelpers
|
5
|
+
def current_session
|
6
|
+
@current_session ||= CoalescingPanda::PersistentSession.find_by(session_key: session_key) if session_key
|
7
|
+
@current_session ||= CoalescingPanda::PersistentSession.create_from_launch(params, current_lti_account.id)
|
8
|
+
@current_session
|
9
|
+
end
|
10
|
+
|
11
|
+
def current_lti_account
|
12
|
+
@account ||= CoalescingPanda::LtiAccount.find_by!(key: organization_key) if organization_key
|
13
|
+
@account ||= CoalescingPanda::LtiAccount.find_by(id: organization_id) if organization_id
|
14
|
+
@account
|
15
|
+
end
|
16
|
+
|
17
|
+
def current_session_data
|
18
|
+
current_session.data
|
19
|
+
end
|
20
|
+
|
21
|
+
def encrypted_session_key
|
22
|
+
msg_encryptor.encrypt_and_sign(current_session.session_key)
|
23
|
+
end
|
24
|
+
|
25
|
+
def save_session
|
26
|
+
current_session.try(:save)
|
27
|
+
end
|
28
|
+
|
29
|
+
def session_changed?
|
30
|
+
current_session.changed? && current_session.changes[:data].present?
|
31
|
+
end
|
5
32
|
|
6
33
|
def canvas_oauth2(*roles)
|
7
34
|
return if have_session?
|
@@ -100,18 +127,19 @@ module CoalescingPanda
|
|
100
127
|
|
101
128
|
def lti_authorize!(*roles)
|
102
129
|
authorized = false
|
103
|
-
if @lti_account = params['oauth_consumer_key'] && LtiAccount.find_by_key(params['oauth_consumer_key'])
|
130
|
+
if (@lti_account = params['oauth_consumer_key'] && LtiAccount.find_by_key(params['oauth_consumer_key']))
|
104
131
|
sanitized_params = sanitize_params
|
105
132
|
authenticator = IMS::LTI::Services::MessageAuthenticator.new(request.original_url, sanitized_params, @lti_account.secret)
|
106
133
|
authorized = authenticator.valid_signature?
|
107
134
|
end
|
108
|
-
logger.info 'not authorized on tp valid request'
|
135
|
+
logger.info 'not authorized on tp valid request' unless authorized
|
109
136
|
authorized = authorized && (roles.count == 0 || (roles & lti_roles).count > 0)
|
110
|
-
logger.info 'not authorized on roles'
|
137
|
+
logger.info 'not authorized on roles' unless authorized
|
111
138
|
authorized = authorized && @lti_account.validate_nonce(params['oauth_nonce'], DateTime.strptime(params['oauth_timestamp'], '%s'))
|
112
|
-
logger.info 'not authorized on nonce'
|
139
|
+
logger.info 'not authorized on nonce' unless authorized
|
113
140
|
render :text => 'Invalid Credentials, please contact your Administrator.', :status => :unauthorized unless authorized
|
114
|
-
|
141
|
+
# create session on first launch
|
142
|
+
current_session
|
115
143
|
authorized
|
116
144
|
end
|
117
145
|
|
@@ -136,26 +164,7 @@ module CoalescingPanda
|
|
136
164
|
end
|
137
165
|
|
138
166
|
def lti_roles
|
139
|
-
@lti_roles ||=
|
140
|
-
case role.downcase.strip
|
141
|
-
when 'admin'
|
142
|
-
:admin
|
143
|
-
when 'urn:lti:instrole:ims/lis/administrator'
|
144
|
-
:admin
|
145
|
-
when 'learner'
|
146
|
-
:student
|
147
|
-
when 'instructor'
|
148
|
-
:teacher
|
149
|
-
when 'urn:lti:role:ims/lis/teachingassistant'
|
150
|
-
:ta
|
151
|
-
when 'contentdeveloper'
|
152
|
-
:designer
|
153
|
-
when 'urn:lti:instrole:ims/lis/observer'
|
154
|
-
:observer
|
155
|
-
else
|
156
|
-
:none
|
157
|
-
end
|
158
|
-
}.uniq
|
167
|
+
@lti_roles ||= current_session_data[:roles]
|
159
168
|
end
|
160
169
|
|
161
170
|
def canvas_environment
|
@@ -168,38 +177,68 @@ module CoalescingPanda
|
|
168
177
|
end
|
169
178
|
|
170
179
|
def session_check
|
171
|
-
logger.warn 'session_check is deprecated. Functionality moved to
|
180
|
+
logger.warn 'session_check is deprecated. Functionality moved to lti_authorize.'
|
172
181
|
end
|
173
182
|
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
183
|
+
private
|
184
|
+
|
185
|
+
def msg_encryptor
|
186
|
+
@crypt ||= ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base[0..31])
|
187
|
+
end
|
188
|
+
|
189
|
+
def organization_key
|
190
|
+
params[:oauth_consumer_key] || (current_session_data[:launch_params][:oauth_consumer_key] if @current_session)
|
191
|
+
end
|
192
|
+
|
193
|
+
def organization_id
|
194
|
+
params[:organization_id] || (current_session_data[:launch_params][:organization_id] if @current_session)
|
195
|
+
end
|
179
196
|
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
session[:safari_cookie_fixed] = true
|
184
|
-
redirect_to params[:platform_redirect_url]
|
185
|
-
return false
|
197
|
+
def session_key
|
198
|
+
if params[:encrypted_session_key]
|
199
|
+
return msg_encryptor.decrypt_and_verify(params[:encrypted_session_key])
|
186
200
|
end
|
187
|
-
|
201
|
+
params[:session_key] || session_key_header
|
188
202
|
end
|
189
203
|
|
190
|
-
def
|
191
|
-
|
192
|
-
|
204
|
+
def session_key_header
|
205
|
+
if (match = request.headers['Authorization'].try(:match, /crypted_token=(.+)/))
|
206
|
+
msg_encryptor.decrypt_and_verify(match[1])
|
207
|
+
elsif (match = request.headers['Authorization'].try(:match, /token=(.+)/))
|
208
|
+
match[1]
|
209
|
+
end
|
193
210
|
end
|
194
211
|
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
212
|
+
# Redirect with the session key intact. In production,
|
213
|
+
# handle this by encrypting the session key. That way if the
|
214
|
+
# url is logged anywhere, it will all be encrypted data. In dev,
|
215
|
+
# just put it in the URL. Putting it in the URL
|
216
|
+
# is insecure, but is fine in development.
|
217
|
+
# Keeping it in the URL in development means that it plays
|
218
|
+
# nicely with webpack-dev-server live reloading (otherwise
|
219
|
+
# you get an access error every time it tries to live reload).
|
220
|
+
|
221
|
+
def redirect_with_session_to(path, id_or_resource = nil, redirect_params = {})
|
222
|
+
if Rails.env.development? || Rails.env.test?
|
223
|
+
redirect_development_mode(path, id_or_resource, redirect_params)
|
199
224
|
else
|
200
|
-
|
225
|
+
redirect_production_mode(path, id_or_resource, redirect_params)
|
201
226
|
end
|
202
227
|
end
|
203
228
|
|
229
|
+
def redirect_development_mode(path, id_or_resource = nil, redirect_params)
|
230
|
+
redirect_to send(path, id_or_resource, {
|
231
|
+
session_key: current_session.session_key,
|
232
|
+
organization_id: current_lti_account.id
|
233
|
+
}.merge(redirect_params))
|
234
|
+
end
|
235
|
+
|
236
|
+
def redirect_production_mode(path, id_or_resource = nil, redirect_params)
|
237
|
+
redirect_to send(path, id_or_resource, {
|
238
|
+
encrypted_session_key: encrypted_session_key,
|
239
|
+
organization_id: current_lti_account.id
|
240
|
+
}.merge(redirect_params))
|
241
|
+
end
|
242
|
+
|
204
243
|
end
|
205
244
|
end
|
@@ -50,6 +50,9 @@ module CoalescingPanda
|
|
50
50
|
# https://github.com/MiniProfiler/rack-mini-profiler/issues/327
|
51
51
|
# DON'T ENABLE THIS FOR PRODUCTION!
|
52
52
|
script_src << "'unsafe-eval'"
|
53
|
+
elsif CoalescingPanda.lti_options.has_key?(:allow_unsafe_eval) && CoalescingPanda.lti_options[:allow_unsafe_eval] == true
|
54
|
+
# For when code is returned from server and injected into dom. Need to have unsafe-eval or it won't work.
|
55
|
+
script_src << "'unsafe-eval'"
|
53
56
|
end
|
54
57
|
|
55
58
|
SecureHeaders::Configuration.default do |config|
|
@@ -80,6 +83,10 @@ module CoalescingPanda
|
|
80
83
|
SecureHeaders::Configuration.override(:safari_override) do |config|
|
81
84
|
config.cookies = SecureHeaders::OPT_OUT
|
82
85
|
end
|
86
|
+
|
87
|
+
SecureHeaders::Configuration.override(:allow_inline_scripts) do |config|
|
88
|
+
config.csp[:script_src] << "'unsafe-inline'"
|
89
|
+
end
|
83
90
|
end
|
84
91
|
|
85
92
|
end
|
data/spec/dummy/db/schema.rb
CHANGED
@@ -173,6 +173,17 @@ ActiveRecord::Schema.define(version: 20160830183155) do
|
|
173
173
|
|
174
174
|
add_index "coalescing_panda_oauth_states", ["state_key"], name: "index_coalescing_panda_oauth_states_on_state_key", unique: true
|
175
175
|
|
176
|
+
create_table "coalescing_panda_persistent_sessions", force: :cascade do |t|
|
177
|
+
t.string "session_key"
|
178
|
+
t.text "data"
|
179
|
+
t.integer "coalescing_panda_lti_account_id"
|
180
|
+
t.datetime "created_at", null: false
|
181
|
+
t.datetime "updated_at", null: false
|
182
|
+
end
|
183
|
+
|
184
|
+
add_index "coalescing_panda_persistent_sessions", ["coalescing_panda_lti_account_id"], name: "index_persistent_session_on_lti_account_id", using: :btree
|
185
|
+
add_index "coalescing_panda_persistent_sessions", ["session_key"], name: "index_coalescing_panda_persistent_sessions_on_session_key", unique: true, using: :btree
|
186
|
+
|
176
187
|
create_table "coalescing_panda_sections", force: :cascade do |t|
|
177
188
|
t.integer "coalescing_panda_course_id", null: false
|
178
189
|
t.string "name"
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coalescing_panda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Mills
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2020-
|
13
|
+
date: 2020-07-22 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -416,6 +416,7 @@ files:
|
|
416
416
|
- app/models/coalescing_panda/workers/course_miner.rb
|
417
417
|
- app/models/coalescing_panda/lti_nonce.rb
|
418
418
|
- app/models/coalescing_panda/course.rb
|
419
|
+
- app/models/coalescing_panda/persistent_session.rb
|
419
420
|
- app/models/coalescing_panda/lti_account.rb
|
420
421
|
- app/models/coalescing_panda/user.rb
|
421
422
|
- app/models/coalescing_panda/group_category.rb
|
@@ -463,6 +464,7 @@ files:
|
|
463
464
|
- db/migrate/20141120153135_create_coalescing_panda_enrollments.rb
|
464
465
|
- db/migrate/20150107205405_create_coalescing_panda_groups.rb
|
465
466
|
- db/migrate/20141119225721_create_coalescing_panda_courses.rb
|
467
|
+
- db/migrate/20200528224505_create_coalescing_panda_persistent_session.rb
|
466
468
|
- db/migrate/20141124160857_create_delayed_jobs.rb
|
467
469
|
- db/migrate/20131119165343_create_coalescing_panda_lti_nonces.rb
|
468
470
|
- db/migrate/20150106180131_add_published_to_assignments.rb
|