api_models 0.1.0

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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/README.md +2 -0
  4. data/lib/api_models/version.rb +5 -0
  5. data/lib/api_models.rb +68 -0
  6. data/lib/models/account.rb +39 -0
  7. data/lib/models/agent.rb +439 -0
  8. data/lib/models/app.rb +18 -0
  9. data/lib/models/app47_cache.rb +69 -0
  10. data/lib/models/app_active_build.rb +32 -0
  11. data/lib/models/app_policy.rb +24 -0
  12. data/lib/models/app_shield_policy.rb +35 -0
  13. data/lib/models/authorized_user_policy.rb +44 -0
  14. data/lib/models/b2b_app.rb +8 -0
  15. data/lib/models/cat/customer.rb +9 -0
  16. data/lib/models/cat/customer_device.rb +10 -0
  17. data/lib/models/cl/customer.rb +9 -0
  18. data/lib/models/cl/customer_device.rb +10 -0
  19. data/lib/models/commerce_app_store.rb +8 -0
  20. data/lib/models/concerns/app47_app_analyzable.rb +46 -0
  21. data/lib/models/concerns/app47_app_buildable.rb +46 -0
  22. data/lib/models/concerns/app47_app_configurable.rb +34 -0
  23. data/lib/models/concerns/app47_app_policies.rb +59 -0
  24. data/lib/models/concerns/app47_cdn_url.rb +50 -0
  25. data/lib/models/concerns/app47_email_sendable.rb +29 -0
  26. data/lib/models/concerns/app47_logger.rb +109 -0
  27. data/lib/models/concerns/checkin_able.rb +28 -0
  28. data/lib/models/concerns/searchable.rb +54 -0
  29. data/lib/models/configuration_constraint.rb +22 -0
  30. data/lib/models/configuration_group.rb +50 -0
  31. data/lib/models/configuration_rule.rb +58 -0
  32. data/lib/models/device.rb +124 -0
  33. data/lib/models/external_app.rb +15 -0
  34. data/lib/models/gehc/customer.rb +17 -0
  35. data/lib/models/gehc/customer_device.rb +23 -0
  36. data/lib/models/group.rb +40 -0
  37. data/lib/models/internal_app.rb +11 -0
  38. data/lib/models/metric_data_job.rb +16 -0
  39. data/lib/models/option.rb +37 -0
  40. data/lib/models/pin_code_policy.rb +50 -0
  41. data/lib/models/platform.rb +26 -0
  42. data/lib/models/platform_model.rb +14 -0
  43. data/lib/models/product_app.rb +22 -0
  44. data/lib/models/product_support_app.rb +19 -0
  45. data/lib/models/queue_manager.rb +67 -0
  46. data/lib/models/redis_configuration.rb +137 -0
  47. data/lib/models/sso_ad_group.rb +7 -0
  48. data/lib/models/sso_ad_server.rb +13 -0
  49. data/lib/models/sso_ad_user.rb +13 -0
  50. data/lib/models/sso_azure_server.rb +18 -0
  51. data/lib/models/sso_directory_group.rb +13 -0
  52. data/lib/models/sso_directory_server.rb +21 -0
  53. data/lib/models/sso_google_server.rb +13 -0
  54. data/lib/models/sso_ldap_group.rb +8 -0
  55. data/lib/models/sso_ldap_rest_server.rb +34 -0
  56. data/lib/models/sso_ldap_server.rb +19 -0
  57. data/lib/models/sso_ldap_user.rb +7 -0
  58. data/lib/models/sso_oauth_server.rb +16 -0
  59. data/lib/models/sso_saml_v2_server.rb +25 -0
  60. data/lib/models/sso_server.rb +38 -0
  61. data/lib/models/sso_user.rb +13 -0
  62. data/lib/models/system_configuration.rb +92 -0
  63. data/lib/models/time_bomb_policy.rb +138 -0
  64. data/lib/models/user.rb +53 -0
  65. data/lib/models/user_app_permission.rb +16 -0
  66. data/lib/models/user_device.rb +83 -0
  67. data/lib/models/version_management_policy.rb +113 -0
  68. data/lib/models/web_app.rb +12 -0
  69. metadata +408 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dc2b42f380187648ed1eb18f3b2faa1ef2719e09e5e36245abb3441bfe90c215
4
+ data.tar.gz: 7db4b2aa8b21342d5fee241631d579033097db73a78e635b71d2d33091756ce2
5
+ SHA512:
6
+ metadata.gz: 9eff6f1c11c250fc24d2799c75975d7f4eab183cf17fbb5da3b162239e14cf144b1dd27437bfcb4f4594886b593d5a8f14928f411cf1659fd0b36110c19ee4ac
7
+ data.tar.gz: edc46b089835c626227297aa2147c3383ed5a22efe210507e737113cd21568c8a48c13b2eda95e527abf55686ab8ff6a7604cc6b5eaaddffec61324dd43e27a4
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2020 App47
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,2 @@
1
+ # api-models
2
+ API Models to be shared by the API Lambda and API Sinatra apps
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ApiModel
4
+ VERSION = '0.1.0'
5
+ end
data/lib/api_models.rb ADDED
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'api_models/version'
4
+
5
+ # Concerns
6
+ require 'models/concerns/app47_app_analyzable.rb'
7
+ require 'models/concerns/app47_app_buildable.rb'
8
+ require 'models/concerns/app47_app_configurable.rb'
9
+ require 'models/concerns/app47_app_policies.rb'
10
+ require 'models/concerns/app47_cdn_url.rb'
11
+ require 'models/concerns/app47_email_sendable.rb'
12
+ require 'models/concerns/app47_logger.rb'
13
+ require 'models/concerns/checkin_able.rb'
14
+ require 'models/concerns/searchable.rb'
15
+
16
+ require 'models/account.rb'
17
+ require 'models/agent.rb'
18
+ require 'models/app.rb'
19
+ require 'models/app47_cache.rb'
20
+ require 'models/app_active_build.rb'
21
+ require 'models/app_policy.rb'
22
+ require 'models/app_shield_policy.rb'
23
+ require 'models/authorized_user_policy.rb'
24
+ require 'models/b2b_app.rb'
25
+ require 'models/product_app.rb'
26
+ require 'models/commerce_app_store.rb'
27
+ require 'models/configuration_constraint.rb'
28
+ require 'models/configuration_group.rb'
29
+ require 'models/configuration_rule.rb'
30
+ require 'models/device.rb'
31
+ require 'models/external_app.rb'
32
+ require 'models/group.rb'
33
+ require 'models/internal_app.rb'
34
+ require 'models/metric_data_job.rb'
35
+ require 'models/option.rb'
36
+ require 'models/pin_code_policy.rb'
37
+ require 'models/platform.rb'
38
+ require 'models/platform_model.rb'
39
+ require 'models/queue_manager.rb'
40
+ require 'models/redis_configuration.rb'
41
+ require 'models/sso_directory_group.rb'
42
+ require 'models/user.rb'
43
+ require 'models/sso_server.rb'
44
+ require 'models/sso_directory_server.rb'
45
+ require 'models/sso_user.rb'
46
+ require 'models/sso_ad_group.rb'
47
+ require 'models/sso_ad_server.rb'
48
+ require 'models/sso_ad_user.rb'
49
+ require 'models/sso_ldap_group.rb'
50
+ require 'models/sso_ldap_rest_server.rb'
51
+ require 'models/sso_ldap_server.rb'
52
+ require 'models/sso_ldap_user.rb'
53
+ require 'models/sso_oauth_server.rb'
54
+ require 'models/sso_azure_server.rb'
55
+ require 'models/sso_google_server.rb'
56
+ require 'models/sso_saml_v2_server.rb'
57
+ require 'models/system_configuration.rb'
58
+ require 'models/time_bomb_policy.rb'
59
+ require 'models/user_app_permission.rb'
60
+ require 'models/user_device.rb'
61
+ require 'models/version_management_policy.rb'
62
+ require 'models/web_app.rb'
63
+ require 'models/cat/customer.rb'
64
+ require 'models/cat/customer_device.rb'
65
+ require 'models/cl/customer.rb'
66
+ require 'models/cl/customer_device.rb'
67
+ require 'models/gehc/customer.rb'
68
+ require 'models/gehc/customer_device.rb'
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Main Account object, should hold references to everything else.
5
+ #
6
+ class Account
7
+ include Mongoid::Document
8
+ include Mongoid::Timestamps
9
+
10
+ #
11
+ # Fields
12
+ #
13
+ field :name, type: String
14
+ field :stage, { type: String, default: 'Trial' }
15
+ field :api_url, type: String
16
+ #
17
+ # Relationships
18
+ #
19
+ has_many :apps do
20
+ #
21
+ # return a list of apps where all the user flag is turned on
22
+ #
23
+ def distinct_all_users
24
+ where(all_users: true)
25
+ end
26
+ end
27
+ has_many :users
28
+ has_many :groups
29
+ has_many :sso_servers
30
+
31
+ #
32
+ # Is the API supported for this account?
33
+ #
34
+ def api_enabled?
35
+ !%w[Terminated Rejected].include?(stage)
36
+ rescue StandardError
37
+ true
38
+ end
39
+ end
@@ -0,0 +1,439 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Represents an agent, a specific install of an app on a device.
5
+ #
6
+ class Agent
7
+ include Mongoid::Document
8
+ include Mongoid::Timestamps
9
+ include CheckinAble
10
+ include App47Logger
11
+ #
12
+ # Fields
13
+ #
14
+ field :agent_version, type: String
15
+ field :app_version, type: String
16
+ field :app_version_code, type: String
17
+ field :app_environment, type: String, default: 'production'
18
+ field :net_new_counted, type: Boolean, default: false
19
+ # field :installed_version, type: String
20
+ #
21
+ # Relationships
22
+ #
23
+ belongs_to :device
24
+ belongs_to :app, inverse_of: :agents, optional: true
25
+ #
26
+ # Call backs
27
+ #
28
+ after_create :notify_new_agent
29
+ before_save :update_app_version
30
+ after_find :migrate_data
31
+ #
32
+ # Delegations
33
+ #
34
+ delegate :platform, to: :device
35
+
36
+ #
37
+ # If the agent is capable of reporting the correct version code, basically is the agent version high enough on the
38
+ # respective platform to have the correct version code to report.
39
+ #
40
+ def use_version_code?
41
+ app.use_version_code?(platform) && (ios? && agent_version.to_f >= 4.4 || android? && agent_version.to_f >= 3.2)
42
+ rescue StandardError
43
+ false
44
+ end
45
+
46
+ #
47
+ # Is this agent an ios agent?
48
+ #
49
+ def ios?
50
+ platform.eql?('iOS')
51
+ end
52
+
53
+ #
54
+ # Is this agent an Android Agent
55
+ #
56
+ def android?
57
+ platform.eql?('Android')
58
+ end
59
+
60
+ #
61
+ # return the configuration for this agent
62
+ #
63
+ def config_data
64
+ data = App47Cache.get(config_cache_key)
65
+ if data.nil?
66
+ data = build_config_data
67
+ else
68
+ data[:server_time_epoch] = Time.now.to_i
69
+ end
70
+
71
+ App47Cache.set(config_cache_key, data, 30)
72
+ data
73
+ end
74
+
75
+ #
76
+ # Fetch or create an agent
77
+ #
78
+ def self.fetch(app, device, json)
79
+ agent = Agent.find_or_create_by(device: device, app: app)
80
+ device.apps << app if agent.new_record? # Add this app to the device if agent is new
81
+ # new_version = json[:app_version]
82
+ # agent.notify_new_app_version(new_version) unless new_version.nil? || new_version.eql?(agent.app_version)
83
+ agent.update_attributes!(json)
84
+ agent
85
+ end
86
+
87
+ #
88
+ # Main entry point for inserting metric data
89
+ #
90
+ def insert_metric_data(type, data, agent_ip)
91
+ # If the agent isn't enabled, then drop the data and return a hearty "Thank you!"
92
+ return true unless app_agent_enabled?
93
+
94
+ send "insert_#{type}_data", data.symbolize_keys, agent_ip
95
+ end
96
+
97
+ #
98
+ # Notify the main worker to update the app and agent configuration information with the new
99
+ # version
100
+ #
101
+ # We also want to force the config to regenerate for the agent as the version will
102
+ # yield different results.
103
+ #
104
+ def notify_new_app_version(new_version)
105
+ # App47Cache.delete config_cache_key
106
+ # App47Cache.delete policies_cache_key
107
+ # QueueManager.push_worker job: 'app_upgrade',
108
+ # app_id: app_id.to_s,
109
+ # agent_id: id.to_s,
110
+ # current_version: app_version,
111
+ # new_version: new_version
112
+ end
113
+
114
+ #
115
+ # Safely update attributes from the document
116
+ #
117
+ def update_attributes!(attributes = {})
118
+ super(filter_json(attributes))
119
+ end
120
+
121
+ #
122
+ # Safely update attributes from the document
123
+ #
124
+ def update!(attributes = {})
125
+ super(filter_json(attributes))
126
+ end
127
+
128
+ #
129
+ # Find the user by the device id associated with the agent's device
130
+ #
131
+ def find_user
132
+ account = app.account
133
+ device_identifier = device.unique_identifier
134
+ device = UserDevice.find_device_by device_identifier
135
+ user = device.user
136
+ raise 'Invalid account' unless account.eql?(user.account)
137
+
138
+ device.user
139
+ rescue StandardError
140
+ nil
141
+ end
142
+
143
+ #
144
+ # filter only the json parameters we want to take into the device. The agent
145
+ # sends up more than we need for the device.
146
+ #
147
+ def filter_json(json)
148
+ filtered_json = {}
149
+ %i[agent_version app_version app_version_code app_environment].each do |field|
150
+ filtered_json[field] = json[field] if json[field].present?
151
+ end
152
+ filtered_json
153
+ end
154
+
155
+ #
156
+ # Encrypt the data unique to this agent.
157
+ #
158
+ def encrypt_data(data)
159
+ cipher = OpenSSL::Cipher::AES256.new(:CBC)
160
+ cipher.encrypt
161
+ user_token = device.unique_identifier
162
+ user_token += user_token while user_token.length < 48
163
+ cipher.key = user_token[0..31]
164
+ cipher.iv = user_token[32..47]
165
+ Base64.encode64(cipher.update(data) + cipher.final)
166
+ end
167
+
168
+ private
169
+
170
+ #
171
+ # Remove analysis_states and modified_at attributes that are no longer used
172
+ #
173
+ def migrate_data
174
+ remove_attribute :analysis_states
175
+ remove_attribute :modified_at
176
+ end
177
+
178
+ #
179
+ # Basic information relavant to any metric
180
+ #
181
+ def base_metric_data(data, agent_ip, session_id = nil)
182
+ if data[:start_time_epoch].nil?
183
+ r_start_time = Time.parse(data[:start_time]).gmtime
184
+ record_raw_time = data[:start_time].to_s.encode('UTF-8')
185
+ time_zone = record_raw_time.split(' ').last
186
+ else
187
+ r_start_time = Time.at(data[:start_time_epoch].to_i).utc
188
+ time_zone = nil
189
+ record_raw_time = nil
190
+ end
191
+
192
+ { agent_id: id.to_s,
193
+ start_time: r_start_time,
194
+ time_zone: time_zone,
195
+ app_id: app_id.to_s,
196
+ record_date: r_start_time.strftime('%D'),
197
+ session_id: data[:session_id] || session_id,
198
+ userinfo: data[:userinfo],
199
+ tags: data[:tags],
200
+ ip_address: agent_ip,
201
+ device_id: device_id.to_s,
202
+ raw_agent_time_str: record_raw_time,
203
+ raw_agent_epoch_str: data[:start_time_epoch],
204
+ loc: {
205
+ lat: (data[:location_lat].nil? ? 0 : data[:location_lat].to_f),
206
+ long: (data[:location_long].nil? ? 0 : data[:location_long].to_f)
207
+ } }
208
+ end
209
+
210
+ #
211
+ # insert log data received from the agent
212
+ #
213
+ def insert_logs_data(data, agent_ip, session_id = nil)
214
+ return if data[:logs].blank?
215
+
216
+ data[:logs].each { |log_data| insert_log_data(log_data.symbolize_keys, agent_ip, session_id) }
217
+ end
218
+
219
+ #
220
+ # insert a single log entry
221
+ #
222
+ def insert_log_data(data, agent_ip, session_id = nil)
223
+ log_level = data[:level].downcase
224
+ QueueManager.push_log_event base_metric_data(data, agent_ip, session_id).merge(
225
+ log_id: data[:uuid],
226
+ level: log_level,
227
+ crash_log_format: data[:crash_log_format],
228
+ is_error_or_crash: %w[error crash].include?(log_level),
229
+ message: safely_encode(data, :message),
230
+ filename: safely_encode(data, :filename),
231
+ line_number: data[:line_number],
232
+ error_name: safely_encode(data, :error_name),
233
+ error_reason: safely_encode(data, :error_reason),
234
+ error_detail: safely_encode(data, :error_detail)
235
+ )
236
+ end
237
+
238
+ #
239
+ # Insert multiple timed events
240
+ #
241
+ def insert_timed_events_data(data, agent_ip, session_id = nil)
242
+ return if data[:timedevents].blank?
243
+
244
+ data[:timedevents].each { |event_data| insert_timed_event_data(event_data.symbolize_keys, agent_ip, session_id) }
245
+ end
246
+
247
+ #
248
+ # Insert a single timed event
249
+ #
250
+ def insert_timed_event_data(data, agent_ip, session_id = nil)
251
+ duration = data[:duration].to_f
252
+ return if duration <= 0
253
+
254
+ QueueManager.push_timed_event base_metric_data(data, agent_ip, session_id).merge(
255
+ timed_event_id: data[:uuid],
256
+ duration: duration,
257
+ name: data[:name],
258
+ created_at_hf: Time.now.gmtime.strftime('%Y-%m-%d %H:%M:%S.%L')
259
+ )
260
+ end
261
+
262
+ #
263
+ # Insert multiple generic events
264
+ #
265
+ def insert_generic_events_data(data, agent_ip, session_id = nil)
266
+ return if data[:events].blank?
267
+
268
+ data[:events].each { |event_data| insert_generic_event_data(event_data.symbolize_keys, agent_ip, session_id) }
269
+ end
270
+
271
+ #
272
+ # Insert a single generic event
273
+ #
274
+ def insert_generic_event_data(data, agent_ip, session_id = nil)
275
+ QueueManager.push_generic_event base_metric_data(data, agent_ip, session_id).merge(event_id: data[:uuid],
276
+ name: data[:name])
277
+ end
278
+
279
+ #
280
+ # Insert a single session event
281
+ #
282
+ # Returns the session id for use elsewhere
283
+ #
284
+ def insert_session_data(data, agent_ip)
285
+ duration = data[:duration].to_f
286
+ return if duration <= 0
287
+
288
+ QueueManager.push_session base_metric_data(data, agent_ip).merge(session_id: data[:uuid], duration: duration)
289
+ data[:uuid]
290
+ end
291
+
292
+ #
293
+ # Insert bulk data which contacts a session, logs, timed events and generic events
294
+ #
295
+ def insert_bulk_data(data, agent_ip)
296
+ session_id = insert_session_data(data[:session].symbolize_keys, agent_ip)
297
+ insert_logs_data(data, agent_ip, session_id)
298
+ insert_generic_events_data(data, agent_ip, session_id)
299
+ insert_timed_events_data(data, agent_ip, session_id)
300
+ metrics = data[:app47logs]
301
+ metrics.each { |app47log| log_debug "AgentLog47: #{id}: #{app47log.inspect}" } if metrics.present?
302
+ end
303
+
304
+ #
305
+ # Build the config data from scratch
306
+ #
307
+ def build_config_data
308
+ data = default_config_data
309
+ return data if app.blank?
310
+
311
+ if app.api_enabled?
312
+ data[:agent_enabled] = true
313
+ data[:configuration_groups] = app.configuration_groups.match_agent(self)
314
+ data[:message] = 'App found and agent enabled'
315
+ if app.agent_capture_sessions?
316
+ data[:capture_sessions] = true
317
+ data[:capture_generic_events] = app.agent_capture_generic_events?
318
+ data[:capture_timed_events] = app.agent_capture_timed_events?
319
+ data[:agent_log_level] = app.agent_log_level
320
+ data[:session_threshold_seconds] = app.agent_session_threshold_seconds
321
+ end
322
+ end
323
+ data[:message] = 'App found and agent disabled'
324
+ data[:upload_on_exit] = app.agent_upload_on_exit
325
+ data[:capture_crash_format] = app.agent_capture_crash_format
326
+ data[:delay_data_upload_interval] = app.agent_delay_data_upload_interval
327
+ data[:configuration_update_frequency] = app.agent_configuration_update_frequency
328
+ data
329
+ end
330
+
331
+ #
332
+ # Build the default config data block
333
+ #
334
+ def default_config_data
335
+ { agent_id: id.to_s,
336
+ agent_log_level: 'None',
337
+ session_data_endpoint: api_url,
338
+ realtime_data_endpoint: api_url,
339
+ upload_on_exit: false,
340
+ show_network_activity: true,
341
+ capture_crash_format: 'none',
342
+ capture_crashes: false,
343
+ capture_generic_events: false,
344
+ agent_enabled: false,
345
+ session_threshold_seconds: 5,
346
+ message: 'Unknown app id, disabling agent',
347
+ capture_timed_events: false,
348
+ capture_sessions: false,
349
+ delay_data_upload_interval: 60,
350
+ pin_code_required: 0,
351
+ configuration_update_frequency: 7,
352
+ configuration_groups: [],
353
+ server_time_epoch: Time.now.to_i }.merge(policies_for_agent)
354
+ end
355
+
356
+ #
357
+ # Return either the agent's account api url or the system configuration one by default
358
+ #
359
+ def api_url
360
+ app.account.api_url.blank? ? SystemConfiguration.api_url : app.account.api_url
361
+ rescue StandardError
362
+ SystemConfiguration.api_url
363
+ end
364
+
365
+ #
366
+ # Key used to store the agent's config
367
+ #
368
+ def policies_cache_key
369
+ @policies_cache_key ||= "agent_policies::#{id}"
370
+ end
371
+
372
+ #
373
+ # Key used to store the agent's config
374
+ #
375
+ def config_cache_key
376
+ @config_cache_key ||= "config_cache::#{id}"
377
+ end
378
+
379
+ #
380
+ # Tell the net new worker to update analytics
381
+ #
382
+ def notify_new_agent
383
+ QueueManager.push_worker job: 'new_agent', app_id: app_id.to_s, agent_id: id.to_s
384
+ end
385
+
386
+ #
387
+ # Safely encode the given string, capturing any error.
388
+ #
389
+ def safely_encode(data, name)
390
+ data[name].force_encoding('ISO-8859-1').encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
391
+ rescue StandardError
392
+ nil
393
+ end
394
+
395
+ #
396
+ # Safely test if the app is enabled, this allows the app to be missing
397
+ #
398
+ def app_agent_enabled?
399
+ app.agent_enabled?
400
+ rescue StandardError
401
+ false
402
+ end
403
+
404
+ #
405
+ # Clear the cache
406
+ #
407
+ def update_app_version
408
+ return unless changed.include?('app_version')
409
+
410
+ (from, to) = changes[:app_version]
411
+ return if from.eql?(to)
412
+
413
+ App47Cache.delete config_cache_key
414
+ App47Cache.delete policies_cache_key
415
+ QueueManager.push_worker job: 'app_upgrade',
416
+ app_id: app_id.to_s,
417
+ agent_id: id.to_s,
418
+ current_version: from,
419
+ new_version: to
420
+ true # Force a return of true so that we don't break the callback chain.
421
+ end
422
+
423
+ #
424
+ # Return a hash of security policies for this agent
425
+ #
426
+ def policies_for_agent
427
+ defaults = { device_enabled: 1, enforce_active_version: false }
428
+ current = App47Cache.get(policies_cache_key)
429
+ return current if current.present?
430
+
431
+ current = app.policies.inject(defaults) { |acc, elem| acc.merge(elem.enforce_policy(self)) }
432
+ App47Cache.set(policies_cache_key, current, 30)
433
+ rescue StandardError
434
+ # Clear it out of cache just in case that's the issue
435
+ App47Cache.delete(policies_cache_key)
436
+ # Return the default set of policies
437
+ { device_enabled: 1, enforce_active_version: false }
438
+ end
439
+ end
data/lib/models/app.rb ADDED
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Base class of app
5
+ #
6
+ class App
7
+ include Mongoid::Document
8
+ include Mongoid::Timestamps
9
+ #
10
+ # Fields
11
+ #
12
+ field :name, type: String
13
+ field :all_users, type: Boolean, default: false
14
+ #
15
+ # Relationships
16
+ #
17
+ belongs_to :account
18
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'singleton'
4
+
5
+ #
6
+ # Public: Provide a common interface to redis cache
7
+ #
8
+ # Examples
9
+ #
10
+ # RedisCacheService.delete("key name")
11
+ # # => the value of the key deleted
12
+ #
13
+ # RedisCacheService.get("key name")
14
+ # # => the value of the key
15
+ #
16
+ class App47Cache
17
+ include Singleton
18
+
19
+ class << self
20
+ #
21
+ # Return the redis connection
22
+ #
23
+ def redis
24
+ @redis ||= Redis.new(RedisConfiguration.load(12))
25
+ end
26
+
27
+ #
28
+ # Delete a key from cache
29
+ #
30
+ # key - the key to delete
31
+ #
32
+ def delete(key)
33
+ redis.del key
34
+ end
35
+
36
+ #
37
+ # Get a key from cache
38
+ #
39
+ # key - the key to fetch, if key is not found, returns nil
40
+ #
41
+ def get(key)
42
+ return nil if ENV['RACK_ENV'].eql?('staging')
43
+
44
+ value = redis.get key
45
+ value.nil? ? nil : Marshal.restore(value)
46
+ rescue StandardError
47
+ nil
48
+ end
49
+
50
+ #
51
+ # Set a key with a value and TTL
52
+ #
53
+ # key - the key to delete
54
+ # value - the value to set
55
+ # ttl(optional) - optional time to live parameter, default is no ttl, live forever
56
+ #
57
+ # Returns the value passed in
58
+ #
59
+ def set(key, value, ttl = nil)
60
+ in_value = Marshal.dump value
61
+ if ttl
62
+ redis.setex key, ttl, in_value
63
+ else
64
+ redis.set key, in_value
65
+ end
66
+ value
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Holds the active build for a given app
5
+ #
6
+ class AppActiveBuild
7
+ include Mongoid::Document
8
+ include Mongoid::Timestamps
9
+ include App47CdnUrl
10
+ #
11
+ # Fields
12
+ #
13
+ field :version
14
+ field :version_code
15
+ field :file_url
16
+ field :environment
17
+ field :platform
18
+ field :enforce, type: Boolean, default: false
19
+ #
20
+ # Relationships
21
+ #
22
+ belongs_to :app, inverse_of: :app_active_builds
23
+
24
+ #
25
+ # Return the selected tracking version
26
+ #
27
+ def display_version
28
+ send(app.tracked_version(platform)) || version
29
+ rescue StandardError
30
+ version
31
+ end
32
+ end