coalescing_panda 5.1.10 → 5.2.0.beta1
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 +3 -11
- data/app/views/coalescing_panda/oauth2/oauth2.html.haml +3 -1
- data/db/migrate/20150714205405_create_coalescing_panda_group_categories.rb +0 -1
- data/lib/coalescing_panda/bearcat_uri.rb +20 -18
- data/lib/coalescing_panda/controller_helpers.rb +94 -80
- data/lib/coalescing_panda/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: bee89db63211898d79a2be426960cb091d7d4b9c384b848587c45744e40471e9
|
4
|
+
data.tar.gz: 06b1367659b15e63d7811a0a4ed6385f8f8925c3fac3767b9b0283bd00bd78b2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c902f68f0ed0685039be21864520fe6c80bc199f74afc897b5a38a38e6ea72b335463c0de6498024871e335cb515a7b68c9fa952a384fdf8cb10da12df5e6f23
|
7
|
+
data.tar.gz: 6cc22dcad22c6c77a9734b818969356e5600eedef854057d06320b05e1692631e4c814106ac8179a83bb39d2bef1e5396a02df95a6f1144046b09b5eaa739b64
|
@@ -15,10 +15,12 @@ module CoalescingPanda
|
|
15
15
|
client_key = lti_account.oauth2_client_key
|
16
16
|
user_id = @oauth_state.data[:user_id]
|
17
17
|
api_domain = @oauth_state.data[:api_domain]
|
18
|
+
prefix = @oauth_state.data[:api_url]
|
18
19
|
@oauth_state.destroy
|
19
|
-
|
20
|
+
|
20
21
|
Rails.logger.info "Creating Bearcat client for auth token retrieval pointed to: #{prefix}"
|
21
22
|
client = Bearcat::Client.new(prefix: prefix)
|
23
|
+
|
22
24
|
token_body = client.retrieve_token(client_id, coalescing_panda.oauth2_redirect_url, client_key, params['code'])
|
23
25
|
auth = CanvasApiAuth.where('user_id = ? and api_domain = ?', user_id, api_domain).first_or_initialize
|
24
26
|
auth.api_token = token_body['access_token']
|
@@ -30,20 +32,10 @@ module CoalescingPanda
|
|
30
32
|
end
|
31
33
|
end
|
32
34
|
|
33
|
-
|
34
35
|
private
|
35
36
|
|
36
|
-
def oauth2_protocol
|
37
|
-
ENV['OAUTH_PROTOCOL'] || (Rails.env.development? ? 'http' : 'https')
|
38
|
-
end
|
39
|
-
|
40
37
|
def retrieve_oauth_state
|
41
38
|
@oauth_state ||= params[:state].present? && OauthState.find_by(state_key: params[:state])
|
42
39
|
end
|
43
|
-
|
44
|
-
def valid_state_token
|
45
|
-
return false unless params['state'].present? && session['state'].present?
|
46
|
-
params['state'] == session['state']
|
47
|
-
end
|
48
40
|
end
|
49
41
|
end
|
@@ -3,7 +3,9 @@
|
|
3
3
|
%h2 Canvas Authentication Required
|
4
4
|
%button.btn-canvas#oauth_btn Authenticate
|
5
5
|
|
6
|
-
%form{id: 'lti_form', action: "#{request.original_url}", method:
|
6
|
+
%form{id: 'lti_form', action: "#{request.original_url}", method: request.method}
|
7
|
+
- unless @lti_params.include?(:session_token) || @lti_params.include?(:session_key)
|
8
|
+
%input{:type =>"hidden", :value=>link_nonce, :name=>"session_token"}
|
7
9
|
- @lti_params.each do |k,v|
|
8
10
|
%input{:type =>"hidden", :value=>"#{v}", :name=>"#{k}"}
|
9
11
|
|
@@ -2,7 +2,6 @@ class CreateCoalescingPandaGroupCategories < CoalescingPanda::MiscHelper::Migrat
|
|
2
2
|
def change
|
3
3
|
create_table :coalescing_panda_group_categories do |t|
|
4
4
|
t.belongs_to :context, polymorphic: true
|
5
|
-
t.string :context_type
|
6
5
|
t.integer :canvas_group_category_id
|
7
6
|
t.string :name
|
8
7
|
|
@@ -1,24 +1,26 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
module CoalescingPanda
|
2
|
+
class BearcatUri
|
3
|
+
attr_accessor :uri
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
def initialize(uri)
|
6
|
+
Rails.logger.info "Parsing Bearcat URI: #{uri}"
|
7
|
+
@uri = URI.parse(uri)
|
8
|
+
end
|
8
9
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
10
|
+
def api_domain
|
11
|
+
if Rails.env.test? or Rails.env.development?
|
12
|
+
uri.port.present? ? URI.encode("#{uri.host}:#{uri.port.to_s}") : uri.host
|
13
|
+
else
|
14
|
+
uri.host
|
15
|
+
end
|
14
16
|
end
|
15
|
-
end
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
def scheme
|
19
|
+
[uri.scheme, '://'].join
|
20
|
+
end
|
20
21
|
|
21
|
-
|
22
|
-
|
22
|
+
def prefix
|
23
|
+
[scheme, api_domain].join
|
24
|
+
end
|
23
25
|
end
|
24
|
-
end
|
26
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'browser'
|
2
2
|
require_relative 'session_replacement'
|
3
|
+
require_relative 'bearcat_uri'
|
3
4
|
|
4
5
|
module CoalescingPanda
|
5
6
|
module ControllerHelpers
|
@@ -14,96 +15,77 @@ module CoalescingPanda
|
|
14
15
|
def current_organization; current_lti_account; end
|
15
16
|
|
16
17
|
def canvas_oauth2(*roles)
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
api_auth = CanvasApiAuth.find_by('user_id = ? and api_domain = ?', user_id, uri.api_domain)
|
26
|
-
if api_auth
|
27
|
-
begin
|
28
|
-
refresh_token(uri, api_auth) if api_auth.expired?
|
29
|
-
@client = Bearcat::Client.new(token: api_auth.api_token, prefix: uri.prefix)
|
30
|
-
@client.user_profile 'self'
|
31
|
-
rescue Footrest::HttpError::BadRequest, Footrest::HttpError::Unauthorized
|
32
|
-
# If we can't retrieve our own user profile, or the token refresh fails, something is awry on the canvas end
|
33
|
-
# and we'll need to go through the oauth flow again
|
34
|
-
render_oauth2_page uri, user_id
|
35
|
-
end
|
36
|
-
else
|
37
|
-
render_oauth2_page uri, user_id
|
38
|
-
end
|
18
|
+
unless valid_session?
|
19
|
+
lti_authorize!(*roles)
|
20
|
+
|
21
|
+
current_session_data['user_id'] = params['user_id']
|
22
|
+
current_session_data['lis_person_sourcedid'] = params['lis_person_sourcedid']
|
23
|
+
current_session_data['oauth_consumer_key'] = params['oauth_consumer_key']
|
24
|
+
current_session_data['custom_canvas_account_id'] = params['custom_canvas_account_id']
|
39
25
|
end
|
26
|
+
|
27
|
+
@lti_account = LtiAccount.find_by_key(current_session_data['oauth_consumer_key']) if current_session_data['oauth_consumer_key']
|
28
|
+
|
29
|
+
require_user_api_client
|
40
30
|
end
|
41
31
|
|
42
|
-
def render_oauth2_page
|
43
|
-
|
44
|
-
|
32
|
+
def render_oauth2_page
|
33
|
+
client_id = current_lti_account.oauth2_client_id
|
34
|
+
client = Bearcat::Client.new(prefix: canvas_api_uri)
|
45
35
|
|
46
|
-
client_id = @lti_account.oauth2_client_id
|
47
|
-
client = Bearcat::Client.new(prefix: uri.prefix)
|
48
36
|
state = SecureRandom.hex(32)
|
49
|
-
OauthState.create! state_key: state, data: {
|
37
|
+
OauthState.create! state_key: state, data: {
|
38
|
+
key: current_session_data.dig(:launch_params, :oauth_consumer_key),
|
39
|
+
user_id: current_session_data.dig(:launch_params, :user_id),
|
40
|
+
api_domain: URI.parse(canvas_api_uri).host,
|
41
|
+
api_url: canvas_api_uri,
|
42
|
+
}
|
43
|
+
|
50
44
|
@canvas_url = client.auth_redirect_url(client_id, resolve_coalescing_panda_url(:oauth2_redirect_url), { state: state })
|
51
45
|
|
52
|
-
|
53
|
-
@lti_params = params.to_hash
|
46
|
+
@lti_params = params.to_unsafe_h
|
54
47
|
@lti_params.delete('action')
|
55
48
|
@lti_params.delete('controller')
|
49
|
+
|
56
50
|
render 'coalescing_panda/oauth2/oauth2', layout: 'coalescing_panda/application'
|
57
51
|
end
|
58
52
|
|
59
|
-
def refresh_token(
|
60
|
-
refresh_client = Bearcat::Client.new(prefix:
|
61
|
-
refresh_body = refresh_client.retrieve_token(
|
62
|
-
|
53
|
+
def refresh_token(api_auth)
|
54
|
+
refresh_client = Bearcat::Client.new(prefix: canvas_api_uri)
|
55
|
+
refresh_body = refresh_client.retrieve_token(
|
56
|
+
current_lti_account.oauth2_client_id,
|
57
|
+
resolve_coalescing_panda_url(:oauth2_redirect_url),
|
58
|
+
current_lti_account.oauth2_client_key,
|
59
|
+
api_auth.refresh_token,
|
60
|
+
'refresh_token'
|
61
|
+
)
|
63
62
|
api_auth.update({ api_token: refresh_body['access_token'], expires_at: (Time.now + refresh_body['expires_in']) })
|
64
63
|
end
|
65
64
|
|
66
|
-
def
|
67
|
-
|
68
|
-
|
69
|
-
api_auth = CanvasApiAuth.find_by(user_id: current_session_data['user_id'], api_domain: uri.api_domain)
|
70
|
-
@lti_account = LtiAccount.find_by(key: current_session_data['oauth_consumer_key'])
|
71
|
-
return if @lti_account.nil? || api_auth.nil? # Not all tools use oauth
|
72
|
-
|
73
|
-
refresh_token(uri, api_auth) if api_auth.expired?
|
74
|
-
rescue Footrest::HttpError::BadRequest
|
75
|
-
render_oauth2_page uri, current_session_data['user_id']
|
76
|
-
end
|
77
|
-
|
78
|
-
def set_session(launch_presentation_return_url)
|
79
|
-
current_session_data['user_id'] = params['user_id']
|
80
|
-
current_session_data['uri'] = launch_presentation_return_url
|
81
|
-
current_session_data['lis_person_sourcedid'] = params['lis_person_sourcedid']
|
82
|
-
current_session_data['oauth_consumer_key'] = params['oauth_consumer_key']
|
83
|
-
current_session_data['custom_canvas_account_id'] = params['custom_canvas_account_id']
|
84
|
-
end
|
85
|
-
|
86
|
-
def have_session?
|
87
|
-
if params['tool_consumer_instance_guid'] && current_session_data['user_id'] != params['user_id']
|
88
|
-
reset_session
|
89
|
-
logger.info("resetting session params")
|
90
|
-
current_session_data['user_id'] = params['user_id']
|
65
|
+
def require_user_api_client
|
66
|
+
if user_api_client.nil?
|
67
|
+
render_oauth2_page
|
91
68
|
end
|
69
|
+
end
|
92
70
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
71
|
+
def user_api_client
|
72
|
+
@user_api_client ||= begin
|
73
|
+
user_id = current_session_data.dig(:launch_params, :user_id)
|
74
|
+
puri = URI.parse(canvas_api_uri)
|
75
|
+
api_auth = CanvasApiAuth.find_by('user_id = ? and api_domain = ?', user_id, puri.host)
|
76
|
+
if api_auth
|
77
|
+
begin
|
78
|
+
refresh_token(api_auth) if api_auth.expired?
|
79
|
+
client = Bearcat::Client.new(token: api_auth.api_token, prefix: canvas_api_uri)
|
80
|
+
client.user_profile('self')
|
81
|
+
client
|
82
|
+
rescue Footrest::HttpError::BadRequest, Footrest::HttpError::Unauthorized
|
83
|
+
# If we can't retrieve our own user profile, or the token refresh fails, something is awry on the canvas end
|
84
|
+
# and we'll need to go through the oauth flow again
|
85
|
+
nil
|
86
|
+
end
|
99
87
|
end
|
100
88
|
end
|
101
|
-
|
102
|
-
@lti_account = LtiAccount.find_by_key(current_session_data['oauth_consumer_key']) if current_session_data['oauth_consumer_key']
|
103
|
-
|
104
|
-
!!@client
|
105
|
-
rescue Footrest::HttpError::Unauthorized
|
106
|
-
false
|
107
89
|
end
|
108
90
|
|
109
91
|
def validate_launch!
|
@@ -111,23 +93,39 @@ module CoalescingPanda
|
|
111
93
|
end
|
112
94
|
|
113
95
|
def lti_authorize!(*roles)
|
96
|
+
return true if valid_session?
|
97
|
+
|
114
98
|
authorized = false
|
115
99
|
if (@lti_account = params['oauth_consumer_key'] && LtiAccount.find_by_key(params['oauth_consumer_key']))
|
116
100
|
sanitized_params = sanitize_params
|
117
101
|
@tp = IMS::LTI::ToolProvider.new(@lti_account.key, @lti_account.secret, sanitized_params)
|
118
102
|
authorized = @tp.valid_request?(request)
|
119
103
|
end
|
104
|
+
|
120
105
|
logger.info 'not authorized on tp valid request' unless authorized
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
106
|
+
|
107
|
+
if authorized && !(authorized = (roles.count == 0 || (roles & lti_roles).count > 0))
|
108
|
+
logger.info 'not authorized on roles'
|
109
|
+
end
|
110
|
+
|
111
|
+
if authorized && !(authorized = @lti_account.validate_nonce(params['oauth_nonce'], DateTime.strptime(params['oauth_timestamp'], '%s')))
|
112
|
+
logger.info 'not authorized on nonce'
|
113
|
+
end
|
114
|
+
|
125
115
|
render :text => 'Invalid Credentials, please contact your Administrator.', :status => :unauthorized unless authorized
|
116
|
+
|
126
117
|
# create session on first launch
|
127
118
|
current_session
|
128
119
|
authorized
|
129
120
|
end
|
130
121
|
|
122
|
+
def valid_session?
|
123
|
+
return false unless current_session(create_missing: false)&.persisted?
|
124
|
+
true
|
125
|
+
rescue SessionNonceMismatch
|
126
|
+
false
|
127
|
+
end
|
128
|
+
|
131
129
|
# code for method taken from panda_pal v 4.0.8
|
132
130
|
# used for safari workaround
|
133
131
|
def sanitize_params
|
@@ -161,11 +159,27 @@ module CoalescingPanda
|
|
161
159
|
end
|
162
160
|
end
|
163
161
|
|
164
|
-
def
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
162
|
+
def canvas_api_uri
|
163
|
+
@canvas_api_uri ||= begin
|
164
|
+
launch_params = current_session_data[:launch_params] || {}
|
165
|
+
uri = 'canvas.instructure.com'
|
166
|
+
if launch_params[:custom_canvas_api_domain].present?
|
167
|
+
uri = launch_params[:custom_canvas_api_domain]
|
168
|
+
else
|
169
|
+
uri = current_lti_account.settings[:launch_presentation_return_url] || launch_params[:launch_presentation_return_url]
|
170
|
+
unless uri.include?('://')
|
171
|
+
referrer = URI.parse(request.env["HTTP_REFERER"])
|
172
|
+
referrer.path = ''
|
173
|
+
uri = [referrer.to_s, uri].join
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
uri = ((Rails.env.test? or Rails.env.development?) ? 'http' : 'https') + '://' + uri unless uri.include?('://')
|
178
|
+
|
179
|
+
uri = URI.parse(uri)
|
180
|
+
uri.path = ''
|
181
|
+
uri.to_s
|
182
|
+
end
|
169
183
|
end
|
170
184
|
|
171
185
|
private
|
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: 5.
|
4
|
+
version: 5.2.0.beta1
|
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: 2021-
|
13
|
+
date: 2021-11-15 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rails
|
@@ -590,9 +590,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
590
590
|
version: '0'
|
591
591
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
592
592
|
requirements:
|
593
|
-
- - "
|
593
|
+
- - ">"
|
594
594
|
- !ruby/object:Gem::Version
|
595
|
-
version:
|
595
|
+
version: 1.3.1
|
596
596
|
requirements: []
|
597
597
|
rubygems_version: 3.0.3
|
598
598
|
signing_key:
|