intercom-rails 0.2.29 → 0.2.30

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
  SHA1:
3
- metadata.gz: 9c6d7c7465ed41a57577b0780f243224a7d678ca
4
- data.tar.gz: 86dbf0fe02d72ab8891670e78a7491dd50b5da42
3
+ metadata.gz: bdbd2516ea8c1d237f5d5eafdfb4b675ea417c91
4
+ data.tar.gz: dd8ec09548f65b7f0487014fc85ce8e5aad158d0
5
5
  SHA512:
6
- metadata.gz: 365a12c308a3e5040fef8a45e798933381d3a752fea0eb68320dc1b5239dbf46938f3e34ae076d2311985eee8e52366ad348a3ccd52e9e0f61053c1161b2c21c
7
- data.tar.gz: dd94d155b10879c11689409fe95a46ad8976091dcf2eb5328a99588c64acea94b2a3aadaadc73b90ea1f6e35943903e6d14d27330cdc4afaa3c3ce705d026a41
6
+ metadata.gz: e89e04c7f56f65e2e94b3c9a074790f2a158142dedf59df9625e6c0e69abfb216ee881a061a513e1f98f40a89240178431e8b2b4175def08d86327e755e57797
7
+ data.tar.gz: 7b753faf389f74991195c5aba2d778af092e577ed2f88646a186f617a01d76f5dd290aec59708cc57dc7b1d1914675b65734432047ddfdfa02b2e358bd1d21f4
@@ -137,6 +137,12 @@ With this option enabled, clicks on any element with an id of `Intercom` will op
137
137
  <a id="Intercom">Support</a>
138
138
  ```
139
139
 
140
+ You can customize the CSS selector, by setting
141
+
142
+ ```ruby
143
+ config.inbox.custom_activator = '.intercom-link'
144
+ ```
145
+
140
146
  You can read more about configuring the messenger in your applications settings, within Intercom.
141
147
 
142
148
  ### Environments
@@ -188,6 +194,58 @@ You can also override `IntercomRails::Config` options such as your `api_secret`,
188
194
  }) %>
189
195
  <% end %>
190
196
  ```
197
+ ### Content Security Policy Level 2 (CSP) support
198
+ As of version 0.2.30 this gem supports CSP, allowing you to whitelist the include code using both nonces and SHA-256 hashes.
199
+ #### Automatic Insertion
200
+ CSP support for automatic insertion exposes two namespaces that can be defined by the user via monkey patching:
201
+ - String CoreExtensions::IntercomRails::AutoInclude.csp_nonce_hook(controller)
202
+ - nil CoreExtensions::IntercomRails::AutoInclude.csp_sha256_hook(controller, SHA-256 whitelist entry)
203
+ For instance, a CSP nonce can be inserted using the [Twitter Secure Headers](https://github.com/twitter/secureheaders) gem with the following code:
204
+ ```ruby
205
+ module CoreExtensions
206
+ module IntercomRails
207
+ module AutoInclude
208
+ def self.csp_nonce_hook(controller)
209
+ SecureHeaders.content_security_policy_script_nonce(controller.request)
210
+ end
211
+ end
212
+ end
213
+ end
214
+ ```
215
+ or, for whitelisting the SHA-256 hash:
216
+ ```ruby
217
+ module CoreExtensions
218
+ module IntercomRails
219
+ module AutoInclude
220
+ def self.csp_sha256_hook(controller, sha256)
221
+ SecureHeaders.append_content_security_policy_directives(controller.request, {script_src: [sha256]})
222
+ end
223
+ end
224
+ end
225
+ end
226
+ ```
227
+ #### Manual Insertion
228
+ CSP is supported in manual insertion as well, the request nonce can be passed as an option:
229
+ ```erb
230
+ <% if logged_in? %>
231
+ <%= intercom_script_tag({
232
+ :app_id => 'your-app-id',
233
+ :user_id => current_user.id,
234
+ :email => current_user.email,
235
+ :name => current_user.name,
236
+ :created_at => current_user.created_at
237
+ }, {
238
+ :secret => 'your-apps-api-secret',
239
+ :widget => {:activator => '#Intercom'},
240
+ :nonce => get_nonce_from_your_csp_framework
241
+ }) %>
242
+ <% end %>
243
+ ```
244
+ The SHA-256 hash is available using `csp_sha256` just after generating the tag itself:
245
+ ```erb
246
+ <%= intercom_script_tag %>
247
+ <% add_entry_to_csp_whitelist(intercom_script_tag.csp_sha256) %>
248
+ ```
191
249
  ## Importing your users
192
250
  To get started faster with Intercom, `IntercomRails` includes a Rake task that will do an initial import of your users:
193
251
 
@@ -18,6 +18,11 @@ module IntercomRails
18
18
  return unless auto_include_filter.include_javascript?
19
19
 
20
20
  auto_include_filter.include_javascript!
21
+
22
+ # User defined method to whitelist the script sha-256 when using CSP
23
+ if defined?(CoreExtensions::IntercomRails::AutoInclude.csp_sha256_hook) == 'method'
24
+ CoreExtensions::IntercomRails::AutoInclude.csp_sha256_hook(controller, auto_include_filter.csp_sha256)
25
+ end
21
26
  end
22
27
 
23
28
  attr_reader :controller
@@ -27,7 +32,7 @@ module IntercomRails
27
32
  end
28
33
 
29
34
  def include_javascript!
30
- response.body = response.body.gsub(CLOSING_BODY_TAG, intercom_script_tag.output + '\\0')
35
+ response.body = response.body.gsub(CLOSING_BODY_TAG, intercom_script_tag.to_s + '\\0')
31
36
  end
32
37
 
33
38
  def include_javascript?
@@ -38,6 +43,10 @@ module IntercomRails
38
43
  intercom_script_tag.valid?
39
44
  end
40
45
 
46
+ def csp_sha256
47
+ intercom_script_tag.csp_sha256
48
+ end
49
+
41
50
  private
42
51
  def response
43
52
  controller.response
@@ -56,7 +65,19 @@ module IntercomRails
56
65
  end
57
66
 
58
67
  def intercom_script_tag
59
- @script_tag ||= ScriptTag.new(:find_current_user_details => true, :find_current_company_details => true, :controller => controller, :show_everywhere => show_everywhere?)
68
+ options = {
69
+ :find_current_user_details => true,
70
+ :find_current_company_details => true,
71
+ :controller => controller,
72
+ :show_everywhere => show_everywhere?
73
+ }
74
+ # User defined method for applying a nonce to the inserted js tag when
75
+ # using CSP
76
+ if defined?(CoreExtensions::IntercomRails::AutoInclude.csp_nonce_hook) == 'method'
77
+ nonce = CoreExtensions::IntercomRails::AutoInclude.csp_nonce_hook(controller)
78
+ options.merge!(:nonce => nonce)
79
+ end
80
+ @script_tag = ScriptTag.new(options)
60
81
  end
61
82
 
62
83
  def show_everywhere?
@@ -103,6 +103,7 @@ module IntercomRails
103
103
 
104
104
  config_group :inbox do
105
105
  config_accessor :counter # Keep this for backwards compatibility
106
+ config_accessor :custom_activator
106
107
  config_accessor :style do |value|
107
108
  raise ArgumentError, "inbox.style must be one of :default or :custom" unless [:default, :custom].include?(value)
108
109
  end
@@ -116,13 +116,17 @@ module IntercomRails
116
116
  if IntercomRails.config.user.model.present?
117
117
  IntercomRails.config.user.model.call
118
118
  else
119
- User
119
+ user_klass_name if user_klass_name.class != Module
120
120
  end
121
121
  rescue NameError
122
122
  # Rails lazy loads constants, so this is how we check
123
123
  nil
124
124
  end
125
125
 
126
+ def user_klass_name
127
+ User
128
+ end
129
+
126
130
  def send_users(users)
127
131
  request = Net::HTTP::Post.new(uri.request_uri)
128
132
  request.basic_auth(IntercomRails.config.app_id, IntercomRails.config.api_key)
@@ -72,7 +72,7 @@ module IntercomRails
72
72
  end
73
73
 
74
74
  def identity_present?
75
- self.class.identity_attributes.any? { |attribute_name| proxied_object.respond_to?(attribute_name) && proxied_object.send(attribute_name).present? }
75
+ self.class.identity_attributes.any? { |attribute_name| (proxied_object.is_a?(Hash) && proxied_object[attribute_name].present?) || (proxied_object.respond_to?(attribute_name) && proxied_object.send(attribute_name).present?) }
76
76
  end
77
77
 
78
78
  def self.proxy_delegator(attribute_name, options = {})
@@ -1,17 +1,16 @@
1
1
  require 'active_support/json'
2
2
  require 'active_support/core_ext/hash/indifferent_access'
3
3
  require 'active_support/core_ext/string/output_safety'
4
+ require 'action_view'
4
5
 
5
6
  module IntercomRails
6
7
 
7
8
  class ScriptTag
8
9
 
9
- def self.generate(*args)
10
- new(*args).output
11
- end
10
+ include ::ActionView::Helpers::JavaScriptHelper
12
11
 
13
12
  attr_reader :user_details, :company_details, :show_everywhere
14
- attr_accessor :secret, :widget_options, :controller
13
+ attr_accessor :secret, :widget_options, :controller, :nonce
15
14
 
16
15
  def initialize(options = {})
17
16
  self.secret = options[:secret] || Config.api_secret
@@ -24,6 +23,7 @@ module IntercomRails
24
23
  elsif options[:user_details]
25
24
  options[:user_details].delete(:company) if options[:user_details]
26
25
  end
26
+ self.nonce = options[:nonce]
27
27
  end
28
28
 
29
29
  def valid?
@@ -31,6 +31,26 @@ module IntercomRails
31
31
  unless @show_everywhere
32
32
  valid = valid && (user_details[:user_id] || user_details[:email]).present?
33
33
  end
34
+ if nonce
35
+ valid = valid && valid_nonce?
36
+ end
37
+ valid
38
+ end
39
+
40
+ def valid_nonce?
41
+ valid = false
42
+ if nonce
43
+ # Base64 regexp:
44
+ # - blocks of 4 [A-Za-z0-9+/]
45
+ # followed either by:
46
+ # - blocks of 2 [A-Za-z0-9+/] + '=='
47
+ # - blocks of 3 [A-Za-z0-9+/] + '='
48
+ base64_regexp = Regexp.new('^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$')
49
+ m = base64_regexp.match(nonce)
50
+ if nonce == m.to_s
51
+ valid = true
52
+ end
53
+ end
34
54
  valid
35
55
  end
36
56
 
@@ -41,17 +61,28 @@ module IntercomRails
41
61
  hsh
42
62
  end
43
63
 
44
- def output
64
+ def to_s
65
+ js_options = 'id="IntercomSettingsScriptTag"'
66
+ if nonce && valid_nonce?
67
+ js_options = js_options + " nonce=\"#{nonce}\""
68
+ end
69
+ str = "<script #{js_options}>#{intercom_javascript}</script>\n"
70
+ str.respond_to?(:html_safe) ? str.html_safe : str
71
+ end
72
+
73
+ def csp_sha256
74
+ base64_sha256 = Base64.encode64(Digest::SHA256.digest(intercom_javascript))
75
+ csp_hash = "'sha256-#{base64_sha256}'".delete("\n")
76
+ csp_hash
77
+ end
78
+
79
+ private
80
+ def intercom_javascript
45
81
  intercom_settings_json = ActiveSupport::JSON.encode(intercom_settings).gsub('<', '\u003C')
46
82
 
47
- str = <<-INTERCOM_SCRIPT
48
- <script id="IntercomSettingsScriptTag">
49
- window.intercomSettings = #{intercom_settings_json};
50
- </script>
51
- <script>(function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',intercomSettings);}else{var d=document;var i=function(){i.c(arguments)};i.q=[];i.c=function(args){i.q.push(args)};w.Intercom=i;function l(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='#{Config.library_url || "https://widget.intercom.io/widget/#{app_id}"}';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);}if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}};})()</script>
52
- INTERCOM_SCRIPT
83
+ str = "window.intercomSettings = #{intercom_settings_json};(function(){var w=window;var ic=w.Intercom;if(typeof ic===\"function\"){ic('reattach_activator');ic('update',intercomSettings);}else{var d=document;var i=function(){i.c(arguments)};i.q=[];i.c=function(args){i.q.push(args)};w.Intercom=i;function l(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='#{Config.library_url || "https://widget.intercom.io/widget/#{j app_id}"}';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);}if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}};})()"
53
84
 
54
- str.respond_to?(:html_safe) ? str.html_safe : str
85
+ str
55
86
  end
56
87
 
57
88
  private
@@ -91,6 +122,7 @@ module IntercomRails
91
122
  end
92
123
 
93
124
  def app_id
125
+ return user_details[:app_id] if user_details[:app_id].present?
94
126
  return IntercomRails.config.app_id if IntercomRails.config.app_id.present?
95
127
  return 'abcd1234' if defined?(Rails) && Rails.env.development?
96
128
 
@@ -100,11 +132,12 @@ module IntercomRails
100
132
  def widget_options_from_config
101
133
  config = {}
102
134
 
135
+ custom_activator = Config.inbox.custom_activator
103
136
  activator = case Config.inbox.style
104
137
  when :default
105
138
  '#IntercomDefaultWidget'
106
139
  when :custom
107
- '#Intercom'
140
+ custom_activator || '#Intercom'
108
141
  else
109
142
  nil
110
143
  end
@@ -13,6 +13,7 @@ module IntercomRails
13
13
  # @option user_details [Hash] :custom_data custom attributes you'd like saved for this user on Intercom.
14
14
  # @option options [String] :widget a hash containing a css selector for an element which when clicked should show the Intercom widget
15
15
  # @option options [String] :secret Your app secret for secure mode
16
+ # @option options [String] :nonce a nonce generated by your CSP framework to be included inside the javascript tag
16
17
  # @return [String] Intercom script tag
17
18
  # @example basic example
18
19
  # <%= intercom_script_tag({ :app_id => "your-app-id",
@@ -33,8 +34,7 @@ module IntercomRails
33
34
  options[:find_current_user_details] = !options[:user_details]
34
35
  options[:find_current_company_details] = !(options[:user_details] && options[:user_details][:company])
35
36
  options[:controller] = controller if defined?(controller)
36
-
37
- ScriptTag.generate(options)
37
+ ScriptTag.new(options)
38
38
  end
39
39
  end
40
40
  end
@@ -1,3 +1,3 @@
1
1
  module IntercomRails
2
- VERSION = "0.2.29"
2
+ VERSION = "0.2.30"
3
3
  end
@@ -111,4 +111,9 @@ IntercomRails.config do |config|
111
111
  # open the messenger.
112
112
  #
113
113
  # config.inbox.style = :custom
114
+ #
115
+ # If you'd like to use your own link activator CSS selector
116
+ # uncomment this line and clicks on any element that matches the query will
117
+ # open the messenger
118
+ # config.inbox.custom_activator = '.intercom'
114
119
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: intercom-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.29
4
+ version: 0.2.30
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben McRedmond
@@ -10,146 +10,146 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2015-10-02 00:00:00.000000000 Z
13
+ date: 2016-02-09 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
17
17
  requirement: !ruby/object:Gem::Requirement
18
18
  requirements:
19
- - - ">"
19
+ - - '>'
20
20
  - !ruby/object:Gem::Version
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
25
25
  requirements:
26
- - - ">"
26
+ - - '>'
27
27
  - !ruby/object:Gem::Version
28
28
  version: '3.0'
29
29
  - !ruby/object:Gem::Dependency
30
30
  name: rake
31
31
  requirement: !ruby/object:Gem::Requirement
32
32
  requirements:
33
- - - ">="
33
+ - - '>='
34
34
  - !ruby/object:Gem::Version
35
35
  version: '0'
36
36
  type: :development
37
37
  prerelease: false
38
38
  version_requirements: !ruby/object:Gem::Requirement
39
39
  requirements:
40
- - - ">="
40
+ - - '>='
41
41
  - !ruby/object:Gem::Version
42
42
  version: '0'
43
43
  - !ruby/object:Gem::Dependency
44
44
  name: actionpack
45
45
  requirement: !ruby/object:Gem::Requirement
46
46
  requirements:
47
- - - ">"
47
+ - - '>'
48
48
  - !ruby/object:Gem::Version
49
49
  version: 3.2.12
50
50
  type: :development
51
51
  prerelease: false
52
52
  version_requirements: !ruby/object:Gem::Requirement
53
53
  requirements:
54
- - - ">"
54
+ - - '>'
55
55
  - !ruby/object:Gem::Version
56
56
  version: 3.2.12
57
57
  - !ruby/object:Gem::Dependency
58
58
  name: rspec
59
59
  requirement: !ruby/object:Gem::Requirement
60
60
  requirements:
61
- - - "~>"
61
+ - - ~>
62
62
  - !ruby/object:Gem::Version
63
63
  version: '3.1'
64
64
  type: :development
65
65
  prerelease: false
66
66
  version_requirements: !ruby/object:Gem::Requirement
67
67
  requirements:
68
- - - "~>"
68
+ - - ~>
69
69
  - !ruby/object:Gem::Version
70
70
  version: '3.1'
71
71
  - !ruby/object:Gem::Dependency
72
72
  name: rspec-rails
73
73
  requirement: !ruby/object:Gem::Requirement
74
74
  requirements:
75
- - - "~>"
75
+ - - ~>
76
76
  - !ruby/object:Gem::Version
77
77
  version: '3.1'
78
78
  type: :development
79
79
  prerelease: false
80
80
  version_requirements: !ruby/object:Gem::Requirement
81
81
  requirements:
82
- - - "~>"
82
+ - - ~>
83
83
  - !ruby/object:Gem::Version
84
84
  version: '3.1'
85
85
  - !ruby/object:Gem::Dependency
86
86
  name: pry
87
87
  requirement: !ruby/object:Gem::Requirement
88
88
  requirements:
89
- - - ">="
89
+ - - '>='
90
90
  - !ruby/object:Gem::Version
91
91
  version: '0'
92
92
  type: :development
93
93
  prerelease: false
94
94
  version_requirements: !ruby/object:Gem::Requirement
95
95
  requirements:
96
- - - ">="
96
+ - - '>='
97
97
  - !ruby/object:Gem::Version
98
98
  version: '0'
99
99
  - !ruby/object:Gem::Dependency
100
100
  name: sinatra
101
101
  requirement: !ruby/object:Gem::Requirement
102
102
  requirements:
103
- - - "~>"
103
+ - - ~>
104
104
  - !ruby/object:Gem::Version
105
105
  version: 1.4.5
106
106
  type: :development
107
107
  prerelease: false
108
108
  version_requirements: !ruby/object:Gem::Requirement
109
109
  requirements:
110
- - - "~>"
110
+ - - ~>
111
111
  - !ruby/object:Gem::Version
112
112
  version: 1.4.5
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: thin
115
115
  requirement: !ruby/object:Gem::Requirement
116
116
  requirements:
117
- - - "~>"
117
+ - - ~>
118
118
  - !ruby/object:Gem::Version
119
119
  version: 1.6.2
120
120
  type: :development
121
121
  prerelease: false
122
122
  version_requirements: !ruby/object:Gem::Requirement
123
123
  requirements:
124
- - - "~>"
124
+ - - ~>
125
125
  - !ruby/object:Gem::Version
126
126
  version: 1.6.2
127
127
  - !ruby/object:Gem::Dependency
128
128
  name: tzinfo
129
129
  requirement: !ruby/object:Gem::Requirement
130
130
  requirements:
131
- - - ">="
131
+ - - '>='
132
132
  - !ruby/object:Gem::Version
133
133
  version: '0'
134
134
  type: :development
135
135
  prerelease: false
136
136
  version_requirements: !ruby/object:Gem::Requirement
137
137
  requirements:
138
- - - ">="
138
+ - - '>='
139
139
  - !ruby/object:Gem::Version
140
140
  version: '0'
141
141
  - !ruby/object:Gem::Dependency
142
142
  name: gem-release
143
143
  requirement: !ruby/object:Gem::Requirement
144
144
  requirements:
145
- - - ">="
145
+ - - '>='
146
146
  - !ruby/object:Gem::Version
147
147
  version: '0'
148
148
  type: :development
149
149
  prerelease: false
150
150
  version_requirements: !ruby/object:Gem::Requirement
151
151
  requirements:
152
- - - ">="
152
+ - - '>='
153
153
  - !ruby/object:Gem::Version
154
154
  version: '0'
155
155
  description: Intercom (https://www.intercom.io) is a customer relationship management
@@ -192,12 +192,12 @@ require_paths:
192
192
  - lib
193
193
  required_ruby_version: !ruby/object:Gem::Requirement
194
194
  requirements:
195
- - - ">="
195
+ - - '>='
196
196
  - !ruby/object:Gem::Version
197
197
  version: '0'
198
198
  required_rubygems_version: !ruby/object:Gem::Requirement
199
199
  requirements:
200
- - - ">="
200
+ - - '>='
201
201
  - !ruby/object:Gem::Version
202
202
  version: '0'
203
203
  requirements: []
@@ -207,4 +207,3 @@ signing_key:
207
207
  specification_version: 4
208
208
  summary: Rails helper for emitting javascript script tags for Intercom
209
209
  test_files: []
210
- has_rdoc: