coalescing_panda 5.1.10 → 5.2.0.beta1

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: acb564123796d263404c131eb71d372a3592d105d822aa843849be8b36ee3474
4
- data.tar.gz: b5cd0d04fda7b01b3b91098a68d27b4d151737d2ee481eff8e0263795d69f248
3
+ metadata.gz: bee89db63211898d79a2be426960cb091d7d4b9c384b848587c45744e40471e9
4
+ data.tar.gz: 06b1367659b15e63d7811a0a4ed6385f8f8925c3fac3767b9b0283bd00bd78b2
5
5
  SHA512:
6
- metadata.gz: 0f75b1ab2b563eb1d512432859c4e54eb592ccdb59b99aa14920a62b725f1edda6d4c3a47d62b37db2d1de67ed4c382247ad2eebaed33eb3f2e55bd4c89d1cbc
7
- data.tar.gz: fbbef643a7ad94950d3ffd9d54cd11f8808bc0ad4d807227eaff73fff1c0f601ea2ff7e58e59fa09db9507b950f1609c8adb3d555e85503bf0301c5a2c5061ed
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
- prefix = [oauth2_protocol, '://', api_domain].join
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: 'post'}
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
- class CoalescingPanda::BearcatUri
2
- attr_accessor :uri
1
+ module CoalescingPanda
2
+ class BearcatUri
3
+ attr_accessor :uri
3
4
 
4
- def initialize(uri)
5
- Rails.logger.info "Parsing Bearcat URI: #{uri}"
6
- @uri = URI.parse(uri)
7
- end
5
+ def initialize(uri)
6
+ Rails.logger.info "Parsing Bearcat URI: #{uri}"
7
+ @uri = URI.parse(uri)
8
+ end
8
9
 
9
- def api_domain
10
- if Rails.env.test? or Rails.env.development?
11
- uri.port.present? ? URI.encode("#{uri.host}:#{uri.port.to_s}") : uri.host
12
- else
13
- uri.host
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
- def scheme
18
- [uri.scheme, '://'].join
19
- end
18
+ def scheme
19
+ [uri.scheme, '://'].join
20
+ end
20
21
 
21
- def prefix
22
- [scheme, api_domain].join
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
- return if have_session?
18
- if lti_authorize!(*roles)
19
- user_id = params['user_id']
20
- launch_presentation_return_url = @lti_account.settings[:launch_presentation_return_url] || params['launch_presentation_return_url']
21
- launch_presentation_return_url = [BearcatUri.new(request.env["HTTP_REFERER"]).prefix, launch_presentation_return_url].join unless launch_presentation_return_url.include?('http')
22
- uri = BearcatUri.new(launch_presentation_return_url)
23
- set_session(launch_presentation_return_url)
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(uri, user_id)
43
- @lti_account = params['oauth_consumer_key'] && LtiAccount.find_by_key(params['oauth_consumer_key'])
44
- return unless @lti_account
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: { key: params['oauth_consumer_key'], user_id: user_id, api_domain: uri.api_domain }
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
- #delete the added params so the original oauth sig still works
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(uri, api_auth)
60
- refresh_client = Bearcat::Client.new(prefix: uri.prefix)
61
- refresh_body = refresh_client.retrieve_token(@lti_account.oauth2_client_id, resolve_coalescing_panda_url(:oauth2_redirect_url),
62
- @lti_account.oauth2_client_key, api_auth.refresh_token, 'refresh_token')
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 check_refresh_token
67
- return unless current_session_data['uri'] && current_session_data['user_id'] && current_session_data['oauth_consumer_key']
68
- uri = BearcatUri.new(current_session_data['uri'])
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
- if (current_session_data['user_id'] && current_session_data['uri'])
94
- uri = BearcatUri.new(current_session_data['uri'])
95
- api_auth = CanvasApiAuth.find_by('user_id = ? and api_domain = ?', current_session_data['user_id'], uri.api_domain)
96
- if api_auth && !api_auth.expired?
97
- @client = Bearcat::Client.new(token: api_auth.api_token, prefix: uri.prefix)
98
- @client.user_profile 'self'
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
- authorized = authorized && (roles.count == 0 || (roles & lti_roles).count > 0)
122
- logger.info 'not authorized on roles' unless authorized
123
- authorized = authorized && @lti_account.validate_nonce(params['oauth_nonce'], DateTime.strptime(params['oauth_timestamp'], '%s'))
124
- logger.info 'not authorized on nonce' unless authorized
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 valid_session?
165
- return false unless current_session(create_missing: false)&.persisted?
166
- true
167
- rescue SessionNonceMismatch
168
- false
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
@@ -1,3 +1,3 @@
1
1
  module CoalescingPanda
2
- VERSION = '5.1.10'
2
+ VERSION = '5.2.0.beta1'
3
3
  end
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.1.10
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-07-21 00:00:00.000000000 Z
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: '0'
595
+ version: 1.3.1
596
596
  requirements: []
597
597
  rubygems_version: 3.0.3
598
598
  signing_key: