api_models 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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