panda_pal 5.0.0.beta.2 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +204 -107
- data/app/controllers/panda_pal/lti_controller.rb +0 -18
- data/app/controllers/panda_pal/lti_v1_p0_controller.rb +34 -0
- data/app/controllers/panda_pal/lti_v1_p3_controller.rb +98 -0
- data/app/lib/lti_xml/base_platform.rb +4 -4
- data/app/lib/panda_pal/launch_url_helpers.rb +69 -0
- data/app/lib/panda_pal/lti_jwt_validator.rb +88 -0
- data/app/lib/panda_pal/misc_helper.rb +11 -0
- data/app/models/panda_pal/organization.rb +21 -47
- data/app/models/panda_pal/organization_concerns/settings_validation.rb +127 -0
- data/app/models/panda_pal/organization_concerns/task_scheduling.rb +204 -0
- data/app/models/panda_pal/platform.rb +40 -0
- data/app/views/panda_pal/lti_v1_p3/login.html.erb +1 -0
- data/app/views/panda_pal/partials/_auto_submit_form.html.erb +9 -0
- data/config/dev_lti_key.key +27 -0
- data/config/routes.rb +12 -2
- data/db/migrate/20160412205931_create_panda_pal_organizations.rb +1 -1
- data/db/migrate/20160413135653_create_panda_pal_sessions.rb +1 -1
- data/db/migrate/20160425130344_add_panda_pal_organization_to_session.rb +1 -1
- data/db/migrate/20170106165533_add_salesforce_id_to_organizations.rb +1 -1
- data/db/migrate/20171205183457_encrypt_organization_settings.rb +1 -1
- data/db/migrate/20171205194657_remove_old_organization_settings.rb +8 -3
- data/lib/panda_pal.rb +28 -15
- data/lib/panda_pal/engine.rb +8 -39
- data/lib/panda_pal/helpers.rb +1 -0
- data/lib/panda_pal/helpers/controller_helper.rb +137 -90
- data/lib/panda_pal/helpers/route_helper.rb +8 -8
- data/lib/panda_pal/helpers/secure_headers.rb +79 -0
- data/lib/panda_pal/version.rb +1 -1
- data/panda_pal.gemspec +6 -2
- data/spec/dummy/config/application.rb +7 -1
- data/spec/dummy/config/environments/development.rb +0 -14
- data/spec/dummy/config/environments/production.rb +0 -11
- data/spec/models/panda_pal/organization/settings_validation_spec.rb +175 -0
- data/spec/models/panda_pal/organization/task_scheduling_spec.rb +144 -0
- data/spec/models/panda_pal/organization_spec.rb +0 -89
- data/spec/spec_helper.rb +4 -0
- metadata +66 -12
- data/app/views/panda_pal/lti/iframe_cookie_authorize.html.erb +0 -19
- data/app/views/panda_pal/lti/iframe_cookie_fix.html.erb +0 -12
- data/spec/dummy/config/initializers/assets.rb +0 -11
data/lib/panda_pal/engine.rb
CHANGED
@@ -38,52 +38,21 @@ module PandaPal
|
|
38
38
|
initializer 'panda_pal.route_options' do |app|
|
39
39
|
ActiveSupport.on_load(:action_controller) do
|
40
40
|
Rails.application.reload_routes!
|
41
|
-
PandaPal::
|
41
|
+
PandaPal::validate_pandapal_config!
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
45
|
initializer :secure_headers do |app|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
if Rails.env.development?
|
50
|
-
# Allow webpack-dev-server to work
|
51
|
-
connect_src << "http://localhost:3035"
|
52
|
-
connect_src << "ws://localhost:3035"
|
53
|
-
|
54
|
-
# Allow stuff like rack-mini-profiler to work in development:
|
55
|
-
# https://github.com/MiniProfiler/rack-mini-profiler/issues/327
|
56
|
-
# DON'T ENABLE THIS FOR PRODUCTION!
|
57
|
-
script_src << "'unsafe-eval'"
|
58
|
-
end
|
59
|
-
|
60
|
-
SecureHeaders::Configuration.default do |config|
|
61
|
-
# The default cookie headers aren't compatable with PandaPal cookies currenntly
|
62
|
-
config.cookies = { samesite: { none: true } }
|
63
|
-
|
64
|
-
if Rails.env.production?
|
65
|
-
config.cookies[:secure] = true
|
46
|
+
begin
|
47
|
+
::SecureHeaders::Configuration.default do |config|
|
48
|
+
PandaPal::SecureHeaders.apply_defaults(config)
|
66
49
|
end
|
67
|
-
|
68
|
-
#
|
69
|
-
config.x_frame_options = "ALLOWALL"
|
70
|
-
|
71
|
-
config.x_content_type_options = "nosniff"
|
72
|
-
config.x_xss_protection = "1; mode=block"
|
73
|
-
config.referrer_policy = %w(origin-when-cross-origin strict-origin-when-cross-origin)
|
74
|
-
|
75
|
-
config.csp = {
|
76
|
-
default_src: %w('self'),
|
77
|
-
script_src: script_src,
|
78
|
-
# Certain CSS-in-JS libraries inline the CSS, so we need to use unsafe-inline for them
|
79
|
-
style_src: %w('self' 'unsafe-inline' blob: https://fonts.googleapis.com),
|
80
|
-
font_src: %w('self' data: https://fonts.gstatic.com),
|
81
|
-
connect_src: connect_src,
|
82
|
-
}
|
50
|
+
rescue ::SecureHeaders::Configuration::AlreadyConfiguredError
|
51
|
+
# The App already applied settings
|
83
52
|
end
|
84
53
|
|
85
|
-
SecureHeaders::Configuration.override(:safari_override) do |config|
|
86
|
-
config.cookies = SecureHeaders::OPT_OUT
|
54
|
+
::SecureHeaders::Configuration.override(:safari_override) do |config|
|
55
|
+
config.cookies = ::SecureHeaders::OPT_OUT
|
87
56
|
end
|
88
57
|
end
|
89
58
|
end
|
data/lib/panda_pal/helpers.rb
CHANGED
@@ -1,13 +1,40 @@
|
|
1
1
|
require 'browser'
|
2
2
|
|
3
3
|
module PandaPal::Helpers::ControllerHelper
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
class SessionNotFound < StandardError; end
|
7
|
+
|
8
|
+
included do
|
9
|
+
helper_method :link_nonce, :current_session
|
10
|
+
|
11
|
+
after_action :auto_save_session
|
12
|
+
end
|
13
|
+
|
4
14
|
def save_session
|
5
15
|
current_session.try(:save)
|
6
16
|
end
|
7
17
|
|
8
18
|
def current_session
|
9
|
-
@current_session
|
10
|
-
|
19
|
+
return @current_session if @current_session.present?
|
20
|
+
|
21
|
+
if params[:session_token]
|
22
|
+
payload = JSON.parse(panda_pal_cryptor.decrypt_and_verify(params[:session_token])).with_indifferent_access
|
23
|
+
matched_session = PandaPal::Session.find_by(session_key: payload[:session_key])
|
24
|
+
|
25
|
+
if matched_session.present? && matched_session.data[:link_nonce] == params[:session_token]
|
26
|
+
@current_session = matched_session
|
27
|
+
@current_session.data[:link_nonce] = nil
|
28
|
+
end
|
29
|
+
elsif (session_key = params[:session_key] || session_key_header || flash[:session_key] || session[:session_key]).present?
|
30
|
+
@current_session = PandaPal::Session.find_by(session_key: session_key) if session_key.present?
|
31
|
+
else
|
32
|
+
@current_session = PandaPal::Session.new(panda_pal_organization_id: current_organization.id)
|
33
|
+
end
|
34
|
+
|
35
|
+
raise SessionNotFound, "Session Not Found" unless @current_session.present?
|
36
|
+
|
37
|
+
@current_session
|
11
38
|
end
|
12
39
|
|
13
40
|
def current_organization
|
@@ -16,17 +43,41 @@ module PandaPal::Helpers::ControllerHelper
|
|
16
43
|
@organization ||= PandaPal::Organization.find_by_name(Apartment::Tenant.current)
|
17
44
|
end
|
18
45
|
|
46
|
+
def current_lti_platform
|
47
|
+
return @current_lti_platform if @current_lti_platform.present?
|
48
|
+
# TODO: (Future) This could be expanded more to take better advantage of the LTI 1.3 Multi-Tenancy model.
|
49
|
+
if (canvas_url = current_organization&.settings&.dig(:canvas, :base_url)).present?
|
50
|
+
@current_lti_platform ||= PandaPal::Platform::Canvas.new(canvas_url)
|
51
|
+
end
|
52
|
+
@current_lti_platform ||= PandaPal::Platform::Canvas.new('http://localhost:3000') if Rails.env.development?
|
53
|
+
@current_lti_platform ||= PandaPal::Platform::CANVAS
|
54
|
+
@current_lti_platform
|
55
|
+
end
|
56
|
+
|
19
57
|
def current_session_data
|
20
58
|
current_session.data
|
21
59
|
end
|
22
60
|
|
61
|
+
def lti_launch_params
|
62
|
+
current_session_data[:launch_params]
|
63
|
+
end
|
64
|
+
|
23
65
|
def session_changed?
|
24
66
|
current_session.changed? && current_session.changes[:data].present?
|
25
67
|
end
|
26
68
|
|
27
69
|
def validate_launch!
|
28
|
-
authorized = false
|
29
70
|
safari_override
|
71
|
+
|
72
|
+
if params[:id_token].present?
|
73
|
+
validate_v1p3_launch
|
74
|
+
elsif params[:oauth_consumer_key].present?
|
75
|
+
validate_v1p0_launch
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def validate_v1p0_launch
|
80
|
+
authorized = false
|
30
81
|
if @organization = params['oauth_consumer_key'] && PandaPal::Organization.find_by_key(params['oauth_consumer_key'])
|
31
82
|
sanitized_params = request.request_parameters
|
32
83
|
# These params come over with a safari-workaround launch. The authenticator doesn't like them, so clean them out.
|
@@ -37,32 +88,42 @@ module PandaPal::Helpers::ControllerHelper
|
|
37
88
|
authenticator = IMS::LTI::Services::MessageAuthenticator.new(request.original_url, sanitized_params, @organization.secret)
|
38
89
|
authorized = authenticator.valid_signature?
|
39
90
|
end
|
40
|
-
|
91
|
+
|
41
92
|
if !authorized
|
42
93
|
render plain: 'Invalid Credentials, please contact your Administrator.', :status => :unauthorized unless authorized
|
43
|
-
return authorized
|
44
|
-
end
|
45
|
-
if require_persistent_session
|
46
|
-
if cookies_need_iframe_fix?(false)
|
47
|
-
fix_iframe_cookies
|
48
|
-
return false
|
49
|
-
end
|
50
|
-
# For safari we may have been launched temporarily full-screen by canvas. This allows us to set the session cookie.
|
51
|
-
# In this case, we should make sure the session cookie is fixed and redirect back to canvas to properly launch the embedded LTI.
|
52
|
-
if params[:platform_redirect_url]
|
53
|
-
session[:safari_cookie_fixed] = true
|
54
|
-
redirect_to params[:platform_redirect_url]
|
55
|
-
return false
|
56
|
-
end
|
57
94
|
end
|
58
|
-
|
95
|
+
|
96
|
+
authorized
|
59
97
|
end
|
60
98
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
99
|
+
def validate_v1p3_launch
|
100
|
+
decoded_jwt = JSON::JWT.decode(params.require(:id_token), :skip_verification)
|
101
|
+
raise JSON::JWT::VerificationFailed, 'error decoding id_token' if decoded_jwt.blank?
|
102
|
+
|
103
|
+
client_id = decoded_jwt['aud']
|
104
|
+
@organization = PandaPal::Organization.find_by!(key: 'PandaPal') # client_id)
|
105
|
+
raise JSON::JWT::VerificationFailed, 'Unrecognized Organization' unless @organization.present?
|
106
|
+
|
107
|
+
decoded_jwt.verify!(current_lti_platform.public_jwks)
|
108
|
+
|
109
|
+
params[:session_key] = params[:state]
|
110
|
+
raise JSON::JWT::VerificationFailed, 'State is invalid' unless current_session_data[:lti_oauth_nonce] == decoded_jwt['nonce']
|
111
|
+
|
112
|
+
jwt_verifier = PandaPal::LtiJwtValidator.new(decoded_jwt, client_id)
|
113
|
+
raise JSON::JWT::VerificationFailed, jwt_verifier.errors unless jwt_verifier.valid?
|
114
|
+
|
115
|
+
@decoded_lti_jwt = decoded_jwt
|
116
|
+
rescue JSON::JWT::VerificationFailed => e
|
117
|
+
payload = Array(e.message)
|
118
|
+
|
119
|
+
render json: {
|
120
|
+
message: [
|
121
|
+
{ errors: payload },
|
122
|
+
{ id_token: params.require(:id_token) },
|
123
|
+
],
|
124
|
+
}, status: :unauthorized
|
125
|
+
|
126
|
+
false
|
66
127
|
end
|
67
128
|
|
68
129
|
def switch_tenant(organization = current_organization, &block)
|
@@ -74,38 +135,18 @@ module PandaPal::Helpers::ControllerHelper
|
|
74
135
|
end
|
75
136
|
end
|
76
137
|
|
77
|
-
# Browsers that prevent 3rd party cookies by default (Safari and IE) run into problems
|
78
|
-
# with CSRF handling because the Rails session cookie isn't set. To fix this, we
|
79
|
-
# redirect the current page to the LTI using JavaScript, which will set the cookie,
|
80
|
-
# and then immediately redirect back to Canvas.
|
81
|
-
def fix_iframe_cookies
|
82
|
-
if params[:safari_cookie_authorized].present?
|
83
|
-
session[:safari_cookie_authorized] = true
|
84
|
-
redirect_to params[:return_to]
|
85
|
-
elsif (session[:safari_cookie_fixed] && !params[:safari_cookie_authorized])
|
86
|
-
render 'panda_pal/lti/iframe_cookie_authorize', layout: false
|
87
|
-
else
|
88
|
-
render 'panda_pal/lti/iframe_cookie_fix', layout: false
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def cookies_need_iframe_fix?(check_authorized=true)
|
93
|
-
if check_authorized
|
94
|
-
return browser.safari? && !request.referrer&.include?('sessionless_launch') && !(session[:safari_cookie_fixed] && session[:safari_cookie_authorized]) && !params[:platform_redirect_url]
|
95
|
-
else
|
96
|
-
return browser.safari? && !request.referrer&.include?('sessionless_launch') && !session[:safari_cookie_fixed] && !params[:platform_redirect_url]
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|
100
138
|
def forbid_access_if_lacking_session
|
101
|
-
|
102
|
-
fix_iframe_cookies
|
103
|
-
else
|
104
|
-
render plain: 'You should do an LTI Tool Launch.', status: :unauthorized unless valid_session?
|
105
|
-
end
|
139
|
+
render plain: 'You should do an LTI Tool Launch.', status: :unauthorized unless valid_session?
|
106
140
|
safari_override
|
107
141
|
end
|
108
142
|
|
143
|
+
def verify_authenticity_token
|
144
|
+
# No need to check CSRF when no cookies were sent. This fixes CSRF failures in Browsers
|
145
|
+
# that restrict Cookie setting within an IFrame.
|
146
|
+
return unless request.cookies.keys.length > 0
|
147
|
+
super
|
148
|
+
end
|
149
|
+
|
109
150
|
def valid_session?
|
110
151
|
[
|
111
152
|
current_session.persisted?,
|
@@ -119,59 +160,65 @@ module PandaPal::Helpers::ControllerHelper
|
|
119
160
|
use_secure_headers_override(:safari_override) if browser.safari?
|
120
161
|
end
|
121
162
|
|
163
|
+
# Redirect with the session key intact. In production,
|
164
|
+
# handle this by adding a one-time use encrypted token to the URL.
|
165
|
+
# Keeping it in the URL in development means that it plays
|
166
|
+
# nicely with webpack-dev-server live reloading (otherwise
|
167
|
+
# you get an access error everytime it tries to live reload).
|
168
|
+
|
169
|
+
def redirect_with_session_to(location, params = {}, route_context: self)
|
170
|
+
if Rails.env.development?
|
171
|
+
redirect_to route_context.send(location, {
|
172
|
+
session_key: current_session.session_key,
|
173
|
+
organization_id: current_organization.id,
|
174
|
+
}.merge(params))
|
175
|
+
else
|
176
|
+
redirect_to route_context.send(location, {
|
177
|
+
session_token: link_nonce,
|
178
|
+
organization_id: current_organization.id,
|
179
|
+
}.merge(params))
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
def link_nonce
|
184
|
+
@link_nonce ||= begin
|
185
|
+
current_session_data[:link_nonce] = SecureRandom.hex
|
186
|
+
|
187
|
+
payload = {
|
188
|
+
session_key: current_session.session_key,
|
189
|
+
organization_id: current_organization.id,
|
190
|
+
nonce: current_session_data[:link_nonce],
|
191
|
+
}
|
192
|
+
|
193
|
+
panda_pal_cryptor.encrypt_and_sign(payload.to_json)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
122
197
|
private
|
198
|
+
|
123
199
|
def organization_key
|
124
|
-
params[:oauth_consumer_key]
|
200
|
+
org_key ||= params[:oauth_consumer_key]
|
201
|
+
org_key ||= "#{params[:client_id]}/#{params[:deployment_id]}" if params[:client_id].present?
|
202
|
+
org_key ||= session[:organization_key]
|
203
|
+
org_key
|
125
204
|
end
|
126
205
|
|
127
206
|
def organization_id
|
128
207
|
params[:organization_id]
|
129
208
|
end
|
130
209
|
|
131
|
-
def session_key
|
132
|
-
if params[:encrypted_session_key]
|
133
|
-
crypt = ActiveSupport::MessageEncryptor.new(Rails.application.secrets.secret_key_base[0..31])
|
134
|
-
return crypt.decrypt_and_verify(params[:encrypted_session_key])
|
135
|
-
end
|
136
|
-
params[:session_key] || session_key_header || flash[:session_key] || session[:session_key]
|
137
|
-
end
|
138
|
-
|
139
210
|
def session_key_header
|
140
211
|
if match = request.headers['Authorization'].try(:match, /token=(.+)/)
|
141
212
|
match[1]
|
142
213
|
end
|
143
214
|
end
|
144
215
|
|
145
|
-
|
146
|
-
|
147
|
-
# url is logged anywhere, it will all be encrypted data. In dev,
|
148
|
-
# just put it in the URL. Putting it in the URL
|
149
|
-
# is insecure, but is fine in development.
|
150
|
-
# Keeping it in the URL in development means that it plays
|
151
|
-
# nicely with webpack-dev-server live reloading (otherwise
|
152
|
-
# you get an access error everytime it tries to live reload).
|
153
|
-
|
154
|
-
def redirect_with_session_to(location, params = {})
|
155
|
-
if Rails.env.development?
|
156
|
-
redirect_development_mode(location, params)
|
157
|
-
else
|
158
|
-
redirect_production_mode(location, params)
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def redirect_development_mode(location, params)
|
163
|
-
redirect_to send(location, {
|
164
|
-
session_key: current_session.session_key,
|
165
|
-
organization_id: current_organization.id
|
166
|
-
}.merge(params))
|
216
|
+
def panda_pal_cryptor
|
217
|
+
@panda_pal_cryptor ||= ActiveSupport::MessageEncryptor.new(Rails.application.secret_key_base[0..31])
|
167
218
|
end
|
168
219
|
|
169
|
-
def
|
170
|
-
|
171
|
-
|
172
|
-
redirect_to send(location, {
|
173
|
-
encrypted_session_key: encrypted_data,
|
174
|
-
organization_id: current_organization.id
|
175
|
-
}.merge(params))
|
220
|
+
def auto_save_session
|
221
|
+
yield if block_given?
|
222
|
+
save_session if @current_session && session_changed?
|
176
223
|
end
|
177
224
|
end
|
@@ -1,17 +1,17 @@
|
|
1
1
|
module PandaPal::Helpers::RouteHelper
|
2
|
-
def lti_nav(
|
2
|
+
def lti_nav(options, *rest, &block)
|
3
3
|
base_path = Rails.application.routes.named_routes[:panda_pal].path.spec
|
4
4
|
raise LtiNavigationInUse.new('PandaPal must be mounted before defining lti_nav routes') if base_path.blank?
|
5
|
-
|
5
|
+
|
6
6
|
nav, to = options.first
|
7
7
|
options[:to] = to
|
8
8
|
options.delete nav
|
9
|
-
lti_options = options.delete(:lti_options) || {}
|
10
9
|
path = "#{base_path}/#{nav.to_s}"
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
10
|
+
|
11
|
+
lti_options = options.delete(:lti_options) || {}
|
12
|
+
lti_options[:route_helper_key] = path.split('/').reject(&:empty?).join('_')
|
13
|
+
post(path, options.dup, &block)
|
14
|
+
get(path, options.dup, &block)
|
15
|
+
PandaPal::stage_navigation(nav, lti_options)
|
16
16
|
end
|
17
17
|
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
module PandaPal
|
2
|
+
module SecureHeaders
|
3
|
+
def self.apply_defaults(config)
|
4
|
+
@config = config
|
5
|
+
# The default cookie headers aren't compatable with PandaPal cookies currenntly
|
6
|
+
config.cookies = { samesite: { none: true } }
|
7
|
+
|
8
|
+
if Rails.env.production?
|
9
|
+
config.cookies[:secure] = true
|
10
|
+
end
|
11
|
+
|
12
|
+
# Need to allow LTI iframes
|
13
|
+
config.x_frame_options = "ALLOWALL"
|
14
|
+
|
15
|
+
config.x_content_type_options = "nosniff"
|
16
|
+
config.x_xss_protection = "1; mode=block"
|
17
|
+
config.referrer_policy = %w(origin-when-cross-origin strict-origin-when-cross-origin)
|
18
|
+
|
19
|
+
config.csp ||= {}
|
20
|
+
|
21
|
+
csp_entry(:default_src, %w['self'])
|
22
|
+
csp_entry(:connect_src, %w['self'])
|
23
|
+
csp_entry(:script_src, %w['self'])
|
24
|
+
|
25
|
+
if Rails.env.development?
|
26
|
+
# Allow webpack-dev-server to work
|
27
|
+
csp_entry(:connect_src, "http://localhost:3035")
|
28
|
+
csp_entry(:connect_src, "ws://localhost:3035")
|
29
|
+
|
30
|
+
# Allow stuff like rack-mini-profiler to work in development:
|
31
|
+
# https://github.com/MiniProfiler/rack-mini-profiler/issues/327
|
32
|
+
# DON'T ENABLE THIS FOR PRODUCTION!
|
33
|
+
csp_entry(:script_src, "'unsafe-eval'")
|
34
|
+
|
35
|
+
# Detect and permit Scout APM in Dev
|
36
|
+
if MiscHelper.to_boolean(ENV['SCOUT_DEV_TRACE'])
|
37
|
+
csp_entry(:default_src, 'https://scoutapm.com')
|
38
|
+
csp_entry(:default_src, 'https://apm.scoutapp.com')
|
39
|
+
|
40
|
+
csp_entry(:script_src, "'unsafe-inline'")
|
41
|
+
csp_entry(:script_src, 'https://scoutapm.com')
|
42
|
+
csp_entry(:script_src, 'https://apm.scoutapp.com')
|
43
|
+
|
44
|
+
csp_entry(:connect_src, 'https://apm.scoutapp.com')
|
45
|
+
|
46
|
+
csp_entry(:style_src, 'https://scoutapm.com')
|
47
|
+
csp_entry(:style_src, 'https://apm.scoutapp.com')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Detect and permit Sentry
|
52
|
+
if defined?(Raven) && Raven.configuration.server.present?
|
53
|
+
csp_entry(:connect_src, Raven.configuration.server)
|
54
|
+
|
55
|
+
# Report CSP Violations to Sentry
|
56
|
+
unless config.csp[:report_uri].present?
|
57
|
+
cfg = Raven.configuration
|
58
|
+
config.csp[:report_uri] = ["#{cfg.scheme}://#{cfg.host}/api/#{cfg.project_id}/security/?sentry_key=#{cfg.public_key}"] unless config.csp[:report_uri].present?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Certain CSS-in-JS libraries inline the CSS, so we need to use unsafe-inline for them
|
63
|
+
csp_entry(:style_src, %w('self' 'unsafe-inline' blob: https://fonts.googleapis.com))
|
64
|
+
csp_entry(:font_src, %w('self' data: https://fonts.gstatic.com))
|
65
|
+
|
66
|
+
@config = nil
|
67
|
+
|
68
|
+
config
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def self.csp_entry(key, *values)
|
74
|
+
values = values.flatten
|
75
|
+
@config.csp[key] ||= []
|
76
|
+
@config.csp[key] |= values
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|