intercom-rails 0.2.29 → 0.2.30

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 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: