panda_pal 5.1.0 → 5.2.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +139 -93
- 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 +13 -0
- data/app/models/panda_pal/organization.rb +6 -3
- data/app/models/panda_pal/{organization → organization_concerns}/settings_validation.rb +21 -5
- data/app/models/panda_pal/{organization → organization_concerns}/task_scheduling.rb +32 -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 +139 -43
- 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 +3 -2
- metadata +32 -8
data/lib/panda_pal.rb
CHANGED
@@ -7,11 +7,11 @@ module PandaPal
|
|
7
7
|
class NotMounted < StandardError;end
|
8
8
|
|
9
9
|
@@lti_navigation = {}
|
10
|
-
@@staged_navigation = {}
|
11
10
|
@@lti_options = {}
|
12
11
|
@@lti_properties = {}
|
13
12
|
@@lti_environments = {}
|
14
13
|
@@lti_custom_params = {}
|
14
|
+
@@lti_private_key = nil
|
15
15
|
|
16
16
|
def self.lti_options= lti_options
|
17
17
|
@@lti_options = lti_options
|
@@ -45,31 +45,44 @@ module PandaPal
|
|
45
45
|
@@lti_custom_params.deep_dup
|
46
46
|
end
|
47
47
|
|
48
|
-
def self.register_navigation(navigation)
|
49
|
-
@@lti_navigation[navigation] ||= {}
|
50
|
-
end
|
51
|
-
|
52
48
|
def self.stage_navigation(navigation, options)
|
53
|
-
@@
|
54
|
-
@@
|
49
|
+
@@lti_navigation[navigation] ||= {}
|
50
|
+
@@lti_navigation[navigation].merge!(options)
|
55
51
|
end
|
56
52
|
|
57
53
|
def self.lti_paths
|
58
54
|
@@lti_navigation.deep_dup
|
59
55
|
end
|
60
56
|
|
61
|
-
def self.
|
62
|
-
@@
|
63
|
-
|
64
|
-
|
65
|
-
|
57
|
+
def self.lti_private_key
|
58
|
+
key = @@lti_private_key.presence
|
59
|
+
key ||= ENV['LTI_PRIVATE_KEY'].presence
|
60
|
+
key ||= File.read(File.join( File.dirname(__FILE__), "../config/dev_lti_key.key")) if Rails.env.development?
|
61
|
+
return nil unless key.present?
|
62
|
+
|
63
|
+
key = OpenSSL::PKey::RSA.new(key) if key.is_a?(String)
|
64
|
+
key
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.lti_private_key=(v)
|
68
|
+
@@lti_private_key = k
|
66
69
|
end
|
67
70
|
|
68
71
|
private
|
69
72
|
|
70
|
-
def self.
|
71
|
-
|
72
|
-
|
73
|
+
def self.validate_pandapal_config!
|
74
|
+
errors = []
|
75
|
+
validate_lti_navigation(errors)
|
76
|
+
if errors.present?
|
77
|
+
lines = errors.map { |e| " - #{e}" }
|
78
|
+
raise "PandaPal was not configured correctly:\n#{lines.join("\n")}"
|
79
|
+
end
|
73
80
|
end
|
74
81
|
|
82
|
+
def self.validate_lti_navigation(errors = [])
|
83
|
+
@@lti_navigation.each do |k, v|
|
84
|
+
errors << "lti navigation '#{k}' does not have a Route!" unless (LaunchUrlHelpers.launch_url(k) rescue nil)
|
85
|
+
end
|
86
|
+
errors
|
87
|
+
end
|
75
88
|
end
|
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,39 @@
|
|
1
1
|
require 'browser'
|
2
2
|
|
3
3
|
module PandaPal::Helpers::ControllerHelper
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
class SessionNonceMismatch < 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
|
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] == payload[:nonce]
|
26
|
+
@current_session = matched_session
|
27
|
+
@current_session.data[:link_nonce] = nil
|
28
|
+
end
|
29
|
+
raise SessionNonceMismatch, "Session Not Found" unless @current_session.present?
|
30
|
+
elsif (session_key = params[:session_key] || session_key_header || flash[:session_key] || session[:session_key]).present?
|
31
|
+
@current_session = PandaPal::Session.find_by(session_key: session_key) if session_key.present?
|
32
|
+
end
|
33
|
+
|
10
34
|
@current_session ||= PandaPal::Session.new(panda_pal_organization_id: current_organization.id)
|
35
|
+
|
36
|
+
@current_session
|
11
37
|
end
|
12
38
|
|
13
39
|
def current_organization
|
@@ -16,17 +42,41 @@ module PandaPal::Helpers::ControllerHelper
|
|
16
42
|
@organization ||= PandaPal::Organization.find_by_name(Apartment::Tenant.current)
|
17
43
|
end
|
18
44
|
|
45
|
+
def current_lti_platform
|
46
|
+
return @current_lti_platform if @current_lti_platform.present?
|
47
|
+
# TODO: (Future) This could be expanded more to take better advantage of the LTI 1.3 Multi-Tenancy model.
|
48
|
+
if (canvas_url = current_organization&.settings&.dig(:canvas, :base_url)).present?
|
49
|
+
@current_lti_platform ||= PandaPal::Platform::Canvas.new(canvas_url)
|
50
|
+
end
|
51
|
+
@current_lti_platform ||= PandaPal::Platform::Canvas.new('http://localhost:3000') if Rails.env.development?
|
52
|
+
@current_lti_platform ||= PandaPal::Platform::CANVAS
|
53
|
+
@current_lti_platform
|
54
|
+
end
|
55
|
+
|
19
56
|
def current_session_data
|
20
57
|
current_session.data
|
21
58
|
end
|
22
59
|
|
60
|
+
def lti_launch_params
|
61
|
+
current_session_data[:launch_params]
|
62
|
+
end
|
63
|
+
|
23
64
|
def session_changed?
|
24
65
|
current_session.changed? && current_session.changes[:data].present?
|
25
66
|
end
|
26
67
|
|
27
68
|
def validate_launch!
|
28
|
-
authorized = false
|
29
69
|
safari_override
|
70
|
+
|
71
|
+
if params[:id_token].present?
|
72
|
+
validate_v1p3_launch
|
73
|
+
elsif params[:oauth_consumer_key].present?
|
74
|
+
validate_v1p0_launch
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def validate_v1p0_launch
|
79
|
+
authorized = false
|
30
80
|
if @organization = params['oauth_consumer_key'] && PandaPal::Organization.find_by_key(params['oauth_consumer_key'])
|
31
81
|
sanitized_params = request.request_parameters
|
32
82
|
# These params come over with a safari-workaround launch. The authenticator doesn't like them, so clean them out.
|
@@ -37,12 +87,42 @@ module PandaPal::Helpers::ControllerHelper
|
|
37
87
|
authenticator = IMS::LTI::Services::MessageAuthenticator.new(request.original_url, sanitized_params, @organization.secret)
|
38
88
|
authorized = authenticator.valid_signature?
|
39
89
|
end
|
40
|
-
|
90
|
+
|
41
91
|
if !authorized
|
42
92
|
render plain: 'Invalid Credentials, please contact your Administrator.', :status => :unauthorized unless authorized
|
43
|
-
return authorized
|
44
93
|
end
|
45
|
-
|
94
|
+
|
95
|
+
authorized
|
96
|
+
end
|
97
|
+
|
98
|
+
def validate_v1p3_launch
|
99
|
+
decoded_jwt = JSON::JWT.decode(params.require(:id_token), :skip_verification)
|
100
|
+
raise JSON::JWT::VerificationFailed, 'error decoding id_token' if decoded_jwt.blank?
|
101
|
+
|
102
|
+
client_id = decoded_jwt['aud']
|
103
|
+
@organization = PandaPal::Organization.find_by!(key: 'PandaPal') # client_id)
|
104
|
+
raise JSON::JWT::VerificationFailed, 'Unrecognized Organization' unless @organization.present?
|
105
|
+
|
106
|
+
decoded_jwt.verify!(current_lti_platform.public_jwks)
|
107
|
+
|
108
|
+
params[:session_key] = params[:state]
|
109
|
+
raise JSON::JWT::VerificationFailed, 'State is invalid' unless current_session_data[:lti_oauth_nonce] == decoded_jwt['nonce']
|
110
|
+
|
111
|
+
jwt_verifier = PandaPal::LtiJwtValidator.new(decoded_jwt, client_id)
|
112
|
+
raise JSON::JWT::VerificationFailed, jwt_verifier.errors unless jwt_verifier.valid?
|
113
|
+
|
114
|
+
@decoded_lti_jwt = decoded_jwt
|
115
|
+
rescue JSON::JWT::VerificationFailed => e
|
116
|
+
payload = Array(e.message)
|
117
|
+
|
118
|
+
render json: {
|
119
|
+
message: [
|
120
|
+
{ errors: payload },
|
121
|
+
{ id_token: params.require(:id_token) },
|
122
|
+
],
|
123
|
+
}, status: :unauthorized
|
124
|
+
|
125
|
+
false
|
46
126
|
end
|
47
127
|
|
48
128
|
def switch_tenant(organization = current_organization, &block)
|
@@ -59,6 +139,13 @@ module PandaPal::Helpers::ControllerHelper
|
|
59
139
|
safari_override
|
60
140
|
end
|
61
141
|
|
142
|
+
def verify_authenticity_token
|
143
|
+
# No need to check CSRF when no cookies were sent. This fixes CSRF failures in Browsers
|
144
|
+
# that restrict Cookie setting within an IFrame.
|
145
|
+
return unless request.cookies.keys.length > 0
|
146
|
+
super
|
147
|
+
end
|
148
|
+
|
62
149
|
def valid_session?
|
63
150
|
[
|
64
151
|
current_session.persisted?,
|
@@ -66,65 +153,74 @@ module PandaPal::Helpers::ControllerHelper
|
|
66
153
|
current_session.panda_pal_organization_id == current_organization.id,
|
67
154
|
Apartment::Tenant.current == current_organization.name
|
68
155
|
].all?
|
156
|
+
rescue SessionNonceMismatch
|
157
|
+
false
|
69
158
|
end
|
70
159
|
|
71
160
|
def safari_override
|
72
161
|
use_secure_headers_override(:safari_override) if browser.safari?
|
73
162
|
end
|
74
163
|
|
164
|
+
# Redirect with the session key intact. In production,
|
165
|
+
# handle this by adding a one-time use encrypted token to the URL.
|
166
|
+
# Keeping it in the URL in development means that it plays
|
167
|
+
# nicely with webpack-dev-server live reloading (otherwise
|
168
|
+
# you get an access error everytime it tries to live reload).
|
169
|
+
|
170
|
+
def redirect_with_session_to(location, params = {}, route_context: self, **rest)
|
171
|
+
params.merge!(rest)
|
172
|
+
if Rails.env.development?
|
173
|
+
redirect_to route_context.send(location, {
|
174
|
+
session_key: current_session.session_key,
|
175
|
+
organization_id: current_organization.id,
|
176
|
+
}.merge(params))
|
177
|
+
else
|
178
|
+
redirect_to route_context.send(location, {
|
179
|
+
session_token: link_nonce,
|
180
|
+
organization_id: current_organization.id,
|
181
|
+
}.merge(params))
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
def link_nonce
|
186
|
+
@link_nonce ||= begin
|
187
|
+
current_session_data[:link_nonce] = SecureRandom.hex
|
188
|
+
|
189
|
+
payload = {
|
190
|
+
session_key: current_session.session_key,
|
191
|
+
organization_id: current_organization.id,
|
192
|
+
nonce: current_session_data[:link_nonce],
|
193
|
+
}
|
194
|
+
|
195
|
+
panda_pal_cryptor.encrypt_and_sign(payload.to_json)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
75
199
|
private
|
200
|
+
|
76
201
|
def organization_key
|
77
|
-
params[:oauth_consumer_key]
|
202
|
+
org_key ||= params[:oauth_consumer_key]
|
203
|
+
org_key ||= "#{params[:client_id]}/#{params[:deployment_id]}" if params[:client_id].present?
|
204
|
+
org_key ||= session[:organization_key]
|
205
|
+
org_key
|
78
206
|
end
|
79
207
|
|
80
208
|
def organization_id
|
81
209
|
params[:organization_id]
|
82
210
|
end
|
83
211
|
|
84
|
-
def session_key
|
85
|
-
if params[:encrypted_session_key]
|
86
|
-
crypt = ActiveSupport::MessageEncryptor.new(Rails.application.secret_key_base[0..31])
|
87
|
-
return crypt.decrypt_and_verify(params[:encrypted_session_key])
|
88
|
-
end
|
89
|
-
params[:session_key] || session_key_header || flash[:session_key] || session[:session_key]
|
90
|
-
end
|
91
|
-
|
92
212
|
def session_key_header
|
93
213
|
if match = request.headers['Authorization'].try(:match, /token=(.+)/)
|
94
214
|
match[1]
|
95
215
|
end
|
96
216
|
end
|
97
217
|
|
98
|
-
|
99
|
-
|
100
|
-
# url is logged anywhere, it will all be encrypted data. In dev,
|
101
|
-
# just put it in the URL. Putting it in the URL
|
102
|
-
# is insecure, but is fine in development.
|
103
|
-
# Keeping it in the URL in development means that it plays
|
104
|
-
# nicely with webpack-dev-server live reloading (otherwise
|
105
|
-
# you get an access error everytime it tries to live reload).
|
106
|
-
|
107
|
-
def redirect_with_session_to(location, params = {})
|
108
|
-
if Rails.env.development?
|
109
|
-
redirect_development_mode(location, params)
|
110
|
-
else
|
111
|
-
redirect_production_mode(location, params)
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
def redirect_development_mode(location, params)
|
116
|
-
redirect_to send(location, {
|
117
|
-
session_key: current_session.session_key,
|
118
|
-
organization_id: current_organization.id
|
119
|
-
}.merge(params))
|
218
|
+
def panda_pal_cryptor
|
219
|
+
@panda_pal_cryptor ||= ActiveSupport::MessageEncryptor.new(Rails.application.secret_key_base[0..31])
|
120
220
|
end
|
121
221
|
|
122
|
-
def
|
123
|
-
|
124
|
-
|
125
|
-
redirect_to send(location, {
|
126
|
-
encrypted_session_key: encrypted_data,
|
127
|
-
organization_id: current_organization.id
|
128
|
-
}.merge(params))
|
222
|
+
def auto_save_session
|
223
|
+
yield if block_given?
|
224
|
+
save_session if @current_session && session_changed?
|
129
225
|
end
|
130
226
|
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
|