web47core 2.0.1 → 2.2.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +111 -41
  3. data/app/controllers/status_controller.rb +8 -3
  4. data/app/helpers/core_form_helper.rb +6 -6
  5. data/app/helpers/core_helper.rb +1 -1
  6. data/app/helpers/core_link_helper.rb +47 -8
  7. data/app/helpers/core_nav_bar_helper.rb +5 -4
  8. data/app/helpers/core_table_helper.rb +80 -0
  9. data/app/helpers/model_modal_helper.rb +3 -3
  10. data/app/views/common/_create_actions.html.haml +12 -0
  11. data/app/views/common/_update_actions.html.haml +10 -0
  12. data/app/views/cron/_edit.html.haml +15 -17
  13. data/app/views/cron/_index.html.haml +74 -67
  14. data/app/views/delayed_job_metrics/_index.html.haml +27 -0
  15. data/app/views/delayed_job_metrics/index.html.haml +1 -0
  16. data/app/views/delayed_job_workers/_index.html.haml +27 -0
  17. data/app/views/delayed_job_workers/index.html.haml +1 -0
  18. data/app/views/delayed_jobs/_index.html.haml +47 -52
  19. data/app/views/delayed_jobs/_show.html.haml +15 -13
  20. data/app/views/system_configurations/_edit.html.haml +14 -9
  21. data/app/views/system_configurations/_show.html.haml +18 -12
  22. data/config/brakeman.ignore +26 -0
  23. data/config/brakeman.yml +2 -0
  24. data/config/locales/en.yml +21 -3
  25. data/lib/app/controllers/concerns/core_delayed_job_metrics_controller.rb +35 -0
  26. data/lib/app/controllers/concerns/core_delayed_job_workers_controller.rb +35 -0
  27. data/lib/app/controllers/concerns/core_delayed_jobs_controller.rb +2 -3
  28. data/lib/app/jobs/cron/command.rb +1 -4
  29. data/lib/app/jobs/cron/record_delayed_job_metrics.rb +25 -0
  30. data/lib/app/jobs/cron/restart_orphaned_delayed_jobs.rb +44 -0
  31. data/lib/app/jobs/cron/server.rb +32 -15
  32. data/lib/app/jobs/cron/switchboard_sync_configuration.rb +2 -0
  33. data/lib/app/jobs/cron/switchboard_sync_models.rb +2 -0
  34. data/lib/app/jobs/cron/trim_collection.rb +1 -1
  35. data/lib/app/jobs/cron/trim_delayed_job_metrics.rb +29 -0
  36. data/lib/app/jobs/cron/trim_delayed_job_workers.rb +39 -0
  37. data/lib/app/jobs/cron/trim_failed_delayed_jobs.rb +2 -0
  38. data/lib/app/models/api_token.rb +9 -0
  39. data/lib/app/models/command_job.rb +12 -11
  40. data/lib/app/models/concerns/api_tokenable.rb +38 -0
  41. data/lib/app/models/concerns/aws_configuration.rb +65 -0
  42. data/lib/app/models/concerns/cdn_url.rb +7 -0
  43. data/lib/app/models/concerns/core_smtp_configuration.rb +65 -0
  44. data/lib/app/models/concerns/core_system_configuration.rb +18 -201
  45. data/lib/app/models/concerns/delayed_job_configuration.rb +24 -0
  46. data/lib/app/models/concerns/email_able.rb +6 -2
  47. data/lib/app/models/concerns/google_sso_configuration.rb +32 -0
  48. data/lib/app/models/concerns/search_able.rb +16 -0
  49. data/lib/app/models/concerns/server_process_able.rb +69 -0
  50. data/lib/app/models/concerns/slack_configuration.rb +38 -0
  51. data/lib/app/models/concerns/standard_model.rb +5 -2
  52. data/lib/app/models/concerns/switchboard_configuration.rb +43 -0
  53. data/lib/app/models/concerns/twilio_configuration.rb +37 -0
  54. data/lib/app/models/concerns/zendesk_configuration.rb +92 -0
  55. data/lib/app/models/{delayed_job.rb → delayed/backend/delayed_job.rb} +41 -0
  56. data/lib/app/models/delayed/jobs/metric.rb +61 -0
  57. data/lib/app/models/delayed/jobs/run.rb +40 -0
  58. data/lib/app/models/delayed/jobs/worker.rb +43 -0
  59. data/lib/app/models/delayed/plugins/time_keeper.rb +33 -0
  60. data/lib/app/models/delayed/worker.rb +24 -0
  61. data/lib/app/models/email_notification.rb +2 -1
  62. data/lib/app/models/email_template.rb +5 -6
  63. data/lib/app/models/notification.rb +12 -2
  64. data/lib/app/models/sms_notification.rb +9 -6
  65. data/lib/app/models/template.rb +12 -12
  66. data/lib/web47core/version.rb +1 -1
  67. data/lib/web47core.rb +33 -9
  68. metadata +114 -69
@@ -1,22 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
4
+ # The System configuration. Various configuration items that can be updated/defined at run time
5
+ #
6
+ # Use of this class allows you to simply ask for the configuration parameter directly without
7
+ # first having to get an instance of it.
8
+ #
9
+ # SystemConfiguration.queue_impl #=> 'RedisQueue'
10
+ #
11
+ # This method only is allowed for accessors, you should NEVER set values on the SystemConfiguration
12
+ # unless you are updating via the Admin or Stack UI, or during testing to setup a specific configuration
13
+ # for that.
14
+ #
3
15
  module CoreSystemConfiguration
4
16
  extend ActiveSupport::Concern
5
- #
6
- # The System configuration. Various configuration items that can be updated/defined at run time
7
- #
8
- # Use of this class allows you to simply ask for the configuration parameter directly without
9
- # first having to get an instance of it.
10
- #
11
- # SystemConfiguration.queue_impl #=> 'RedisQueue'
12
- #
13
- # This method only is allowed for accessors, you should NEVER set values on the SystemConfiguration
14
- # unless you are updating via the Admin or Stack UI, or during testing to setup a specific configuration
15
- # for that.
16
- #
17
+
17
18
  def self.included(base)
18
19
  base.class_eval do
19
20
  attr_accessor :configuration
21
+
20
22
  #
21
23
  # Fields
22
24
  #
@@ -28,79 +30,31 @@ module CoreSystemConfiguration
28
30
  field :short_cache, { type: Integer, default: 1 }
29
31
  field :medium_cache, { type: Integer, default: 5 }
30
32
  field :long_cache, { type: Integer, default: 15 }
31
- # SMTP configuration
32
- field :default_email, type: String, default: 'support@app47.com'
33
- field :support_email, type: String, default: 'support@app47.com'
34
- field :smtp_name, type: String
35
- field :smtp_address, type: String
36
- field :smtp_domain, type: String
37
- field :smtp_port, type: Integer, default: 587
38
- field :smtp_user_name, type: String
39
- field :smtp_password, type: String
40
- field :smtp_enable_starttls_auto, type: Boolean
41
- field :mailgun_api_key, type: String
42
- # Twillio support
43
- field :twilio_account_id, type: String
44
- field :twilio_auth_token, type: String
45
- field :twilio_phone_number, type: String
46
33
  # URLs
47
34
  field :base_url, type: String
48
35
  field :cdn_url, type: String
49
- # Slack support
50
- field :slack_api_url, type: String
51
- field :slack_support_channel, type: String, default: 'support'
52
- field :slack_sales_channel, type: String, default: 'sales'
36
+ field :asset_cdn_url, type: String
53
37
  # Time Zone Support
54
38
  field :default_time_zone, type: String, default: 'US/Eastern'
55
- # AWS
56
- field :aws_region, type: String
57
- field :aws_access_key_id, type: String
58
- field :aws_secret_access_key, type: String
59
- field :aws_auto_scaling_group_name, type: String
60
- # Zendesk
61
- field :zendesk_token, type: String
62
- field :zendesk_base_url, type: String, default: 'https://app47.zendesk.com'
63
- field :zendesk_documentation_path, type: String, default: 'hc'
64
- field :zendesk_support_path, type: String, default: 'hc/en-us/requests'
65
- field :zendesk_updates_path, type: String, default: 'hc'
66
- # Switchboard
67
- field :switchboard_base_url, type: String, default: 'https://switchboard.app47.com'
68
- field :switchboard_stack_id, type: String
69
- field :switchboard_stack_api_token, type: String
70
- field :switchboard_last_sync_at, type: Time
71
39
  # TTLs
72
40
  field :user_model_audit_log_ttl, type: Integer, default: 365
73
- field :slack_notification_ttl, type: Integer, default: 5
74
- field :email_notification_ttl, type: Integer, default: 180
75
41
  #
76
42
  # Validations
77
43
  #
78
44
  validates :environment, presence: true, uniqueness: true
79
- validates :slack_support_channel, presence: true
80
- validates :slack_sales_channel, presence: true
81
45
  validates :default_time_zone, presence: true
82
- validates :switchboard_base_url, url: true
83
- validates :zendesk_base_url, url: true
84
46
  end
85
47
  base.extend ClassMethods
86
48
  end
87
49
 
50
+ #
51
+ # Class methods for SystemConfiguration
52
+ #
88
53
  module ClassMethods
89
54
  def configuration
90
55
  SystemConfiguration.find_or_create_by!(environment: Rails.env)
91
56
  end
92
57
 
93
- def smtp_configuration
94
- output = {}
95
- config = configuration
96
- fields = %w[name address domain port user_name password enable_starttls_auto]
97
- fields.each do |field|
98
- field_name = "smtp_#{field}".to_sym
99
- output[field.to_sym] = config.send(field_name)
100
- end
101
- output
102
- end
103
-
104
58
  #
105
59
  # NOTE: Currently ignored Codacy issue: When using 'method_missing', fall back on 'super'
106
60
  #
@@ -121,18 +75,6 @@ module CoreSystemConfiguration
121
75
  end
122
76
  end
123
77
 
124
- #
125
- # Make sure the password doesn't get blanked out on an update
126
- #
127
- def secure_fields
128
- super + %i[smtp_password
129
- aws_access_secret
130
- mailgun_api_key
131
- switchboard_stack_api_token
132
- twilio_auth_token
133
- zendesk_token]
134
- end
135
-
136
78
  #
137
79
  # Cache times in minutes
138
80
  #
@@ -147,129 +89,4 @@ module CoreSystemConfiguration
147
89
  def long_cache_time
148
90
  long_cache.minutes
149
91
  end
150
-
151
- #
152
- # Determine if SMTP is configured
153
- #
154
- def smtp_configured?
155
- smtp_name.present? && smtp_address.present? && smtp_domain.present?
156
- end
157
-
158
- #
159
- # Determine if mailgun is configured
160
- #
161
- def mail_gun_configured?
162
- smtp_configured? && mailgun_api_key.present?
163
- end
164
-
165
- #
166
- # Determine if AWS is configured
167
- #
168
- def aws_configured?
169
- [aws_region.present?, aws_access_key_id.present?, aws_secret_access_key.present?].all?
170
- end
171
-
172
- #
173
- # Determine if auto scaling group is configured
174
- #
175
- def aws_auto_scaling_configured?
176
- aws_configured? && aws_auto_scaling_group_name.present?
177
- end
178
-
179
- #
180
- # Return the zendesk documentation URL
181
- #
182
- def zendesk_documentation_url(user = nil)
183
- zendesk_url(forward_to: zendesk_documentation_path, user: user)
184
- end
185
-
186
- #
187
- # Return the zendesk support URL
188
- #
189
- def zendesk_requests_url(user = nil)
190
- zendesk_url(forward_to: zendesk_support_path, user: user)
191
- end
192
-
193
- #
194
- # Return the zendesk support URL
195
- #
196
- def zendesk_new_request_url(user = nil)
197
- zendesk_url(forward_to: "#{zendesk_support_path}/new", user: user)
198
- end
199
-
200
- #
201
- # Return the zendesk update URL
202
- #
203
- def zendesk_updates_url(user = nil)
204
- zendesk_url(forward_to: zendesk_updates_path, user: user)
205
- end
206
-
207
- #
208
- # Generate a Zendesk URL
209
- #
210
- # If a user is passed in and Zendesk is configured then return a JWT enabled URL for
211
- # SSO authentication to Zendesk.
212
- #
213
- def zendesk_url(forward_to: zendesk_documentation_path, user: nil)
214
- config = SystemConfiguration.configuration
215
- path = if config.zendesk_configured? && user.present?
216
- time_now = Time.now.to_i
217
- jti = "#{time_now}/#{rand(36 ** 64).to_s(36)}"
218
- payload = { jwt: JWT.encode({ iat: time_now, # Seconds since epoch, determine when this token is stale
219
- jti: jti, # Unique token identifier, helps prevent replay attacks
220
- name: user.name,
221
- email: user.email }, config.zendesk_token),
222
- return_to: CGI.escape([config.zendesk_base_url, forward_to].join('/')) }
223
- ['access/jwt', payload.to_query].join('?')
224
- else
225
- forward_to
226
- end
227
- [config.zendesk_base_url, path].join('/')
228
- end
229
-
230
- #
231
- # Is zendesk configured?
232
- #
233
- def zendesk_configured?
234
- [zendesk_token.present?,
235
- zendesk_base_url.present?,
236
- zendesk_documentation_path.present?,
237
- zendesk_support_path.present?].all?
238
- end
239
-
240
- #
241
- # Public: Determine if switchboard is configured
242
- #
243
- # Examples
244
- #
245
- # switchboard_configured?
246
- # # => true || false
247
- #
248
- def switchboard_configured?
249
- [switchboard_base_url.present?, switchboard_stack_api_token.present?, switchboard_stack_id.present?].all?
250
- end
251
-
252
- #
253
- # Determine if twillio is configured at a system configuration
254
- #
255
- # Examples
256
- #
257
- # switchboard_configured?
258
- # # => true || false
259
- #
260
- def twilio_configured?
261
- [twilio_account_id.present?, twilio_auth_token.present?, twilio_phone_number.present?].all?
262
- end
263
-
264
- #
265
- # Determine if Slack is configured
266
- #
267
- # Examples
268
- #
269
- # switchboard_configured?
270
- # # => true || false
271
- #
272
- def slack_configured?
273
- slack_api_url.present?
274
- end
275
92
  end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # The Slack Configuration to be used inside of SystemConfiguration
5
+ #
6
+ module DelayedJobConfiguration
7
+ extend ActiveSupport::Concern
8
+
9
+ def self.included(base)
10
+ base.class_eval do
11
+ #
12
+ # Fields
13
+ #
14
+ field :delayed_job_max_allowed_method, type: String, default: 'max'
15
+ field :delayed_job_max_allowed_factor, type: Integer, default: 5
16
+ field :delayed_job_restart_orphaned, type: Boolean, default: false
17
+ #
18
+ # Validations
19
+ #
20
+ validates :delayed_job_max_allowed_method, inclusion: { in: %w[max min avg] }
21
+ validates :delayed_job_max_allowed_factor, numericality: { greater_than: 0 }
22
+ end
23
+ end
24
+ end
@@ -61,7 +61,7 @@ module EmailAble
61
61
  #
62
62
  # Reset the bounced email
63
63
  #
64
- def reset_bounce_status
64
+ def reset_bounce_status(current_member = nil)
65
65
  return unless SystemConfiguration.mail_gun_configured? && email_bounced?
66
66
 
67
67
  reset_url = "https://api.mailgun.net/v3/#{SystemConfiguration.smtp_domain}/bounces/#{CGI.escape(email)}"
@@ -69,7 +69,11 @@ module EmailAble
69
69
  rescue RestClient::Exception => error
70
70
  log_error "Unable to reset email bounce status: #{inspect}", error
71
71
  ensure
72
- set email_bounced_at: nil, email_bounce_reason: nil
72
+ if current_member.present?
73
+ update_and_log current_member, email_bounced_at: nil, email_bounce_reason: nil
74
+ else
75
+ set email_bounced_at: nil, email_bounce_reason: nil
76
+ end
73
77
  end
74
78
 
75
79
  #
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Google SSO Configuration
5
+ #
6
+ module GoogleSsoConfiguration
7
+ extend ActiveSupport::Concern
8
+
9
+ def self.included(base)
10
+ base.class_eval do
11
+ #
12
+ # Fields
13
+ #
14
+ field :google_client_id, type: String
15
+ field :google_client_secret, type: String
16
+ end
17
+ end
18
+
19
+ #
20
+ # Make sure the password doesn't get blanked out on an update
21
+ #
22
+ def secure_fields
23
+ super + %i[google_client_secret]
24
+ end
25
+
26
+ #
27
+ # Determine if AWS is configured
28
+ #
29
+ def google_sso_configured?
30
+ google_client_id.present? && google_client_secret.present?
31
+ end
32
+ end
@@ -39,6 +39,22 @@ module SearchAble
39
39
  end
40
40
  end
41
41
 
42
+ def method_missing(method, *args)
43
+ if method.to_s.start_with? 'sorted_'
44
+ send(method.to_s.sub(/^sorted_/, '')).asc(:sort_text)
45
+ else
46
+ super
47
+ end
48
+ end
49
+
50
+ def respond_to_missing?(method_name, include_private = false)
51
+ super || method_name.to_s.start_with?('sorted_')
52
+ end
53
+
54
+ def respond_to?(method, include_private = false)
55
+ super || method.to_s.start_with?('sorted_')
56
+ end
57
+
42
58
  #
43
59
  # Place holder to call to allow for work to be done before we gather up search text fields
44
60
  #
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Mixin for objects that act as servers
5
+ #
6
+ module ServerProcessAble
7
+ extend ActiveSupport::Concern
8
+
9
+ def self.included(base)
10
+ base.class_eval do
11
+ #
12
+ # Fields
13
+ #
14
+ field :host_name, type: String
15
+ field :pid, type: Integer
16
+ field :running, type: Boolean, default: false
17
+ field :last_check_in_at, type: Time
18
+ #
19
+ # Validations
20
+ #
21
+ validates :host_name, presence: true
22
+ validates :pid, presence: true
23
+ end
24
+ base.extend ClassMethods
25
+ end
26
+
27
+ #
28
+ # Methods for the concrete class this concern is attached too, ways to find or create or just find the
29
+ # associated server.
30
+ #
31
+ module ClassMethods
32
+ #
33
+ # Find a record for this server
34
+ #
35
+ def find_or_create_server
36
+ find_or_create_by!(host_name: Socket.gethostname, pid: Process.pid)
37
+ end
38
+
39
+ #
40
+ # Find a worker, return nil if not found
41
+ #
42
+ def find_server
43
+ find_by(host_name: Socket.gethostname, pid: Process.pid)
44
+ rescue StandardError
45
+ nil
46
+ end
47
+ end
48
+
49
+ #
50
+ # Perform a check in for the server
51
+ #
52
+ def check_in
53
+ set(last_check_in_at: Time.now.utc)
54
+ end
55
+
56
+ #
57
+ # Stop the worker
58
+ #
59
+ def stop
60
+ set(running: false, last_check_in_at: Time.now.utc)
61
+ end
62
+
63
+ #
64
+ # Start the worker
65
+ #
66
+ def start
67
+ set(running: true, last_check_in_at: Time.now.utc)
68
+ end
69
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # The Slack Configuration to be used inside of SystemConfiguration
5
+ #
6
+ module SlackConfiguration
7
+ extend ActiveSupport::Concern
8
+
9
+ def self.included(base)
10
+ base.class_eval do
11
+ #
12
+ # Fields
13
+ #
14
+ field :slack_api_url, type: String
15
+ field :slack_support_channel, type: String, default: 'support'
16
+ field :slack_sales_channel, type: String, default: 'sales'
17
+ field :slack_notification_ttl, type: Integer, default: 5
18
+ #
19
+ # Validations
20
+ #
21
+ validates :slack_api_url, url: true, allow_blank: true
22
+ validates :slack_support_channel, presence: true
23
+ validates :slack_sales_channel, presence: true
24
+ end
25
+ end
26
+
27
+ #
28
+ # Determine if Slack is configured
29
+ #
30
+ # Examples
31
+ #
32
+ # slack_configured??
33
+ # # => true || false
34
+ #
35
+ def slack_configured?
36
+ [slack_api_url.present?, slack_support_channel.present?, slack_sales_channel.present?].all?
37
+ end
38
+ end
@@ -42,9 +42,12 @@ module StandardModel
42
42
  # Used by calling 'model.without_callback(*.args, &block) do'
43
43
  def without_callback(*args, &_block)
44
44
  skip_callback(*args)
45
- yield
46
- ensure
45
+ block_result = yield
47
46
  set_callback(*args)
47
+ block_result
48
+ rescue StandardError
49
+ set_callback(*args)
50
+ nil
48
51
  end
49
52
 
50
53
  #
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Switchboard configuration, both the fields and methods needed to determine configuration
5
+ #
6
+ module SwitchboardConfiguration
7
+ extend ActiveSupport::Concern
8
+ #
9
+ # Switchboard configuration
10
+ #
11
+ def self.included(base)
12
+ base.class_eval do
13
+ # Switchboard
14
+ field :switchboard_base_url, type: String, default: 'https://switchboard.app47.com'
15
+ field :switchboard_stack_id, type: String
16
+ field :switchboard_stack_api_token, type: String
17
+ field :switchboard_last_sync_at, type: Time
18
+ #
19
+ # Validations
20
+ #
21
+ validates :switchboard_base_url, url: true
22
+ end
23
+ end
24
+
25
+ #
26
+ # Make sure the password doesn't get blanked out on an update
27
+ #
28
+ def secure_fields
29
+ super + %i[switchboard_stack_api_token]
30
+ end
31
+
32
+ #
33
+ # Public: Determine if switchboard is configured
34
+ #
35
+ # Examples
36
+ #
37
+ # switchboard_configured?
38
+ # # => true || false
39
+ #
40
+ def switchboard_configured?
41
+ [switchboard_base_url.present?, switchboard_stack_api_token.present?, switchboard_stack_id.present?].all?
42
+ end
43
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Twilio configuration, both fields and methods to determine configuration
5
+ #
6
+ module TwilioConfiguration
7
+ extend ActiveSupport::Concern
8
+ #
9
+ # The Twilio Configuration
10
+ #
11
+ def self.included(base)
12
+ base.class_eval do
13
+ field :twilio_account_id, type: String
14
+ field :twilio_auth_token, type: String
15
+ field :twilio_phone_number, type: String
16
+ end
17
+ end
18
+
19
+ #
20
+ # Make sure the password doesn't get blanked out on an update
21
+ #
22
+ def secure_fields
23
+ super + %i[twilio_auth_token]
24
+ end
25
+
26
+ #
27
+ # Determine if twilio is configured at a system configuration
28
+ #
29
+ # Examples
30
+ #
31
+ # twilio_configured??
32
+ # # => true || false
33
+ #
34
+ def twilio_configured?
35
+ [twilio_account_id.present?, twilio_auth_token.present?, twilio_phone_number.present?].all?
36
+ end
37
+ end
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Add zendesk capability to an object to integrate via JWT with Zendesk. Most commonly this will be added
5
+ # to SystemConfiguration, however this can also be added to a different object (i.e., switchboard).
6
+ #
7
+ module ZendeskConfiguration
8
+ extend ActiveSupport::Concern
9
+ def self.included(base)
10
+ base.class_eval do
11
+ #
12
+ # Fields
13
+ #
14
+ field :zendesk_token, type: String
15
+ field :zendesk_base_url, type: String, default: 'https://app47.zendesk.com'
16
+ field :zendesk_documentation_path, type: String, default: 'hc'
17
+ field :zendesk_support_path, type: String, default: 'hc/en-us/requests'
18
+ field :zendesk_updates_path, type: String, default: 'hc'
19
+ #
20
+ # Validations
21
+ #
22
+ validates :zendesk_base_url, url: true
23
+ end
24
+ end
25
+
26
+ #
27
+ # Make sure the password doesn't get blanked out on an update
28
+ #
29
+ def secure_fields
30
+ super + %i[zendesk_token]
31
+ end
32
+
33
+ #
34
+ # Return the zendesk documentation URL
35
+ #
36
+ def zendesk_documentation_url(user = nil)
37
+ zendesk_url(forward_to: zendesk_documentation_path, user: user)
38
+ end
39
+
40
+ #
41
+ # Return the zendesk support URL
42
+ #
43
+ def zendesk_requests_url(user = nil)
44
+ zendesk_url(forward_to: zendesk_support_path, user: user)
45
+ end
46
+
47
+ #
48
+ # Return the zendesk support URL
49
+ #
50
+ def zendesk_new_request_url(user = nil)
51
+ zendesk_url(forward_to: "#{zendesk_support_path}/new", user: user)
52
+ end
53
+
54
+ #
55
+ # Return the zendesk update URL
56
+ #
57
+ def zendesk_updates_url(user = nil)
58
+ zendesk_url(forward_to: zendesk_updates_path, user: user)
59
+ end
60
+
61
+ #
62
+ # Generate a Zendesk URL
63
+ #
64
+ # If a user is passed in and Zendesk is configured then return a JWT enabled URL for
65
+ # SSO authentication to Zendesk.
66
+ #
67
+ def zendesk_url(forward_to: zendesk_documentation_path, user: nil)
68
+ path = if zendesk_configured? && user.present?
69
+ time_now = Time.now.to_i
70
+ jti = "#{time_now}/#{rand(36**64).to_s(36)}"
71
+ payload = { jwt: JWT.encode({ iat: time_now, # Seconds since epoch, determine when this token is stale
72
+ jti: jti, # Unique token identifier, helps prevent replay attacks
73
+ name: user.name,
74
+ email: user.email }, zendesk_token),
75
+ return_to: CGI.escape([zendesk_base_url, forward_to].join('/')) }
76
+ ['access/jwt', payload.to_query].join('?')
77
+ else
78
+ forward_to
79
+ end
80
+ [zendesk_base_url, path].join('/')
81
+ end
82
+
83
+ #
84
+ # Is zendesk configured?
85
+ #
86
+ def zendesk_configured?
87
+ [zendesk_token.present?,
88
+ zendesk_base_url.present?,
89
+ zendesk_documentation_path.present?,
90
+ zendesk_support_path.present?].all?
91
+ end
92
+ end