api_models 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +2 -0
- data/lib/api_models/version.rb +5 -0
- data/lib/api_models.rb +68 -0
- data/lib/models/account.rb +39 -0
- data/lib/models/agent.rb +439 -0
- data/lib/models/app.rb +18 -0
- data/lib/models/app47_cache.rb +69 -0
- data/lib/models/app_active_build.rb +32 -0
- data/lib/models/app_policy.rb +24 -0
- data/lib/models/app_shield_policy.rb +35 -0
- data/lib/models/authorized_user_policy.rb +44 -0
- data/lib/models/b2b_app.rb +8 -0
- data/lib/models/cat/customer.rb +9 -0
- data/lib/models/cat/customer_device.rb +10 -0
- data/lib/models/cl/customer.rb +9 -0
- data/lib/models/cl/customer_device.rb +10 -0
- data/lib/models/commerce_app_store.rb +8 -0
- data/lib/models/concerns/app47_app_analyzable.rb +46 -0
- data/lib/models/concerns/app47_app_buildable.rb +46 -0
- data/lib/models/concerns/app47_app_configurable.rb +34 -0
- data/lib/models/concerns/app47_app_policies.rb +59 -0
- data/lib/models/concerns/app47_cdn_url.rb +50 -0
- data/lib/models/concerns/app47_email_sendable.rb +29 -0
- data/lib/models/concerns/app47_logger.rb +109 -0
- data/lib/models/concerns/checkin_able.rb +28 -0
- data/lib/models/concerns/searchable.rb +54 -0
- data/lib/models/configuration_constraint.rb +22 -0
- data/lib/models/configuration_group.rb +50 -0
- data/lib/models/configuration_rule.rb +58 -0
- data/lib/models/device.rb +124 -0
- data/lib/models/external_app.rb +15 -0
- data/lib/models/gehc/customer.rb +17 -0
- data/lib/models/gehc/customer_device.rb +23 -0
- data/lib/models/group.rb +40 -0
- data/lib/models/internal_app.rb +11 -0
- data/lib/models/metric_data_job.rb +16 -0
- data/lib/models/option.rb +37 -0
- data/lib/models/pin_code_policy.rb +50 -0
- data/lib/models/platform.rb +26 -0
- data/lib/models/platform_model.rb +14 -0
- data/lib/models/product_app.rb +22 -0
- data/lib/models/product_support_app.rb +19 -0
- data/lib/models/queue_manager.rb +67 -0
- data/lib/models/redis_configuration.rb +137 -0
- data/lib/models/sso_ad_group.rb +7 -0
- data/lib/models/sso_ad_server.rb +13 -0
- data/lib/models/sso_ad_user.rb +13 -0
- data/lib/models/sso_azure_server.rb +18 -0
- data/lib/models/sso_directory_group.rb +13 -0
- data/lib/models/sso_directory_server.rb +21 -0
- data/lib/models/sso_google_server.rb +13 -0
- data/lib/models/sso_ldap_group.rb +8 -0
- data/lib/models/sso_ldap_rest_server.rb +34 -0
- data/lib/models/sso_ldap_server.rb +19 -0
- data/lib/models/sso_ldap_user.rb +7 -0
- data/lib/models/sso_oauth_server.rb +16 -0
- data/lib/models/sso_saml_v2_server.rb +25 -0
- data/lib/models/sso_server.rb +38 -0
- data/lib/models/sso_user.rb +13 -0
- data/lib/models/system_configuration.rb +92 -0
- data/lib/models/time_bomb_policy.rb +138 -0
- data/lib/models/user.rb +53 -0
- data/lib/models/user_app_permission.rb +16 -0
- data/lib/models/user_device.rb +83 -0
- data/lib/models/version_management_policy.rb +113 -0
- data/lib/models/web_app.rb +12 -0
- 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
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
|
data/lib/models/agent.rb
ADDED
@@ -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
|