securenative 0.1.20 → 0.1.26

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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +0 -1
  3. data/Gemfile.lock +76 -82
  4. data/README.md +45 -40
  5. data/lib/securenative/api_manager.rb +41 -0
  6. data/lib/securenative/config/configuration_builder.rb +29 -0
  7. data/lib/securenative/config/configuration_manager.rb +58 -0
  8. data/lib/securenative/config/securenative_options.rb +25 -0
  9. data/lib/securenative/context/hanami_context.rb +44 -0
  10. data/lib/securenative/context/rails_context.rb +46 -0
  11. data/lib/securenative/context/securenative_context.rb +69 -0
  12. data/lib/securenative/context/sinatra_context.rb +44 -0
  13. data/lib/securenative/enums/api_route.rb +8 -0
  14. data/lib/securenative/enums/event_types.rb +25 -0
  15. data/lib/securenative/enums/failover_strategy.rb +8 -0
  16. data/lib/securenative/enums/risk_level.rb +9 -0
  17. data/lib/securenative/errors/securenative_config_error.rb +6 -0
  18. data/lib/securenative/errors/securenative_http_error.rb +6 -0
  19. data/lib/securenative/errors/securenative_invalid_options_error.rb +6 -0
  20. data/lib/securenative/errors/securenative_invalid_uri_error.rb +6 -0
  21. data/lib/securenative/errors/securenative_parse_error.rb +6 -0
  22. data/lib/securenative/errors/securenative_sdk_Illegal_state_error.rb +6 -0
  23. data/lib/securenative/errors/securenative_sdk_error.rb +6 -0
  24. data/lib/securenative/event_manager.rb +159 -0
  25. data/lib/securenative/http/secure_native_http_response.rb +14 -0
  26. data/lib/securenative/http/securenative_http_client.rb +52 -0
  27. data/lib/securenative/models/client_token.rb +14 -0
  28. data/lib/securenative/models/device.rb +12 -0
  29. data/lib/securenative/models/event_options.rb +39 -0
  30. data/lib/securenative/models/request_context.rb +20 -0
  31. data/lib/securenative/models/request_options.rb +14 -0
  32. data/lib/securenative/models/sdk_event.rb +51 -0
  33. data/lib/securenative/models/user_traits.rb +15 -0
  34. data/lib/securenative/models/verify_result.rb +18 -0
  35. data/lib/securenative/sdk.rb +85 -0
  36. data/lib/securenative/utils/date_utils.rb +11 -0
  37. data/lib/securenative/utils/encryption_utils.rb +51 -0
  38. data/lib/securenative/utils/ip_utils.rb +25 -0
  39. data/lib/securenative/utils/request_utils.rb +71 -0
  40. data/lib/securenative/utils/secure_native_logger.rb +46 -0
  41. data/lib/securenative/utils/signature_utils.rb +18 -0
  42. data/lib/securenative/utils/utils.rb +11 -0
  43. data/lib/securenative/utils/version_utils.rb +13 -0
  44. data/out/production/securenative-ruby/api_manager.rb +13 -5
  45. data/out/production/securenative-ruby/config/configuration_builder.rb +6 -9
  46. data/out/production/securenative-ruby/config/configuration_manager.rb +24 -23
  47. data/out/production/securenative-ruby/config/securenative_options.rb +8 -5
  48. data/{lib → out/production/securenative-ruby}/context/hanami_context.rb +9 -2
  49. data/{lib → out/production/securenative-ruby}/context/rails_context.rb +4 -1
  50. data/out/production/securenative-ruby/context/securenative_context.rb +35 -8
  51. data/{lib → out/production/securenative-ruby}/context/sinatra_context.rb +11 -4
  52. data/out/production/securenative-ruby/event_manager.rb +15 -14
  53. data/{lib → out/production/securenative-ruby}/http/secure_native_http_response.rb +0 -0
  54. data/out/production/securenative-ruby/http/securenative_http_client.rb +23 -5
  55. data/out/production/securenative-ruby/models/event_options.rb +23 -1
  56. data/out/production/securenative-ruby/models/request_context.rb +2 -2
  57. data/out/production/securenative-ruby/models/sdk_event.rb +22 -6
  58. data/out/production/securenative-ruby/models/user_traits.rb +1 -1
  59. data/out/production/securenative-ruby/models/verify_result.rb +5 -1
  60. data/out/production/securenative-ruby/securenative.rb +2 -10
  61. data/out/production/securenative-ruby/utils/date_utils.rb +1 -1
  62. data/out/production/securenative-ruby/utils/encryption_utils.rb +38 -24
  63. data/out/production/securenative-ruby/utils/request_utils.rb +53 -7
  64. data/out/production/securenative-ruby/utils/secure_native_logger.rb +6 -6
  65. data/out/production/securenative-ruby/utils/version_utils.rb +5 -6
  66. data/out/test/securenative-ruby/spec_api_manager.rb +37 -31
  67. data/out/test/securenative-ruby/spec_context_builder.rb +52 -34
  68. data/out/test/securenative-ruby/spec_encryption_utils.rb +13 -13
  69. data/out/test/securenative-ruby/spec_event_manager.rb +49 -15
  70. data/out/test/securenative-ruby/spec_helper.rb +8 -0
  71. data/out/test/securenative-ruby/spec_request_utils.rb +25 -0
  72. data/out/test/securenative-ruby/spec_sdk_event.rb +24 -0
  73. data/out/test/securenative-ruby/spec_securenative.rb +35 -39
  74. data/out/test/securenative-ruby/spec_securenative_http_client.rb +13 -5
  75. data/out/test/securenative-ruby/spec_signature_utils.rb +1 -1
  76. data/out/test/securenative-ruby/spec_version_util.rb +10 -0
  77. data/securenative.gemspec +16 -14
  78. metadata +48 -45
  79. data/VERSION +0 -1
  80. data/lib/api_manager.rb +0 -35
  81. data/lib/config/configuration_builder.rb +0 -30
  82. data/lib/config/configuration_manager.rb +0 -55
  83. data/lib/config/securenative_options.rb +0 -22
  84. data/lib/context/securenative_context.rb +0 -67
  85. data/lib/enums/api_route.rb +0 -6
  86. data/lib/enums/event_types.rb +0 -23
  87. data/lib/enums/failover_strategy.rb +0 -6
  88. data/lib/enums/risk_level.rb +0 -7
  89. data/lib/errors/securenative_config_error.rb +0 -4
  90. data/lib/errors/securenative_http_error.rb +0 -4
  91. data/lib/errors/securenative_invalid_options_error.rb +0 -4
  92. data/lib/errors/securenative_invalid_uri_error.rb +0 -4
  93. data/lib/errors/securenative_parse_error.rb +0 -4
  94. data/lib/errors/securenative_sdk_Illegal_state_error.rb +0 -4
  95. data/lib/errors/securenative_sdk_error.rb +0 -4
  96. data/lib/event_manager.rb +0 -156
  97. data/lib/event_options.rb +0 -32
  98. data/lib/http/securenative_http_client.rb +0 -38
  99. data/lib/models/client_token.rb +0 -12
  100. data/lib/models/device.rb +0 -10
  101. data/lib/models/event_options.rb +0 -15
  102. data/lib/models/request_context.rb +0 -18
  103. data/lib/models/request_options.rb +0 -12
  104. data/lib/models/sdk_event.rb +0 -35
  105. data/lib/models/user_traits.rb +0 -13
  106. data/lib/models/verify_result.rb +0 -12
  107. data/lib/securenative.rb +0 -95
  108. data/lib/utils/date_utils.rb +0 -9
  109. data/lib/utils/encryption_utils.rb +0 -35
  110. data/lib/utils/ip_utils.rb +0 -23
  111. data/lib/utils/request_utils.rb +0 -54
  112. data/lib/utils/secure_native_logger.rb +0 -44
  113. data/lib/utils/signature_utils.rb +0 -16
  114. data/lib/utils/utils.rb +0 -9
  115. data/lib/utils/version_utils.rb +0 -12
  116. data/out/production/securenative-ruby/event_options.rb +0 -32
  117. data/out/production/securenative-ruby/http/http_response.rb +0 -12
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securenative/enums/failover_strategy'
4
+
5
+ module SecureNative
6
+ class ConfigurationBuilder
7
+ attr_reader :api_key, :api_url, :interval, :max_events, :timeout, :auto_send, :disable, :log_level, :fail_over_strategy, :proxy_headers
8
+ attr_writer :api_key, :api_url, :interval, :max_events, :timeout, :auto_send, :disable, :log_level, :fail_over_strategy, :proxy_headers
9
+
10
+ def initialize(api_key: nil, api_url: 'https://api.securenative.com/collector/api/v1', interval: 1000,
11
+ max_events: 1000, timeout: 1500, auto_send: true, disable: false, log_level: 'FATAL',
12
+ fail_over_strategy: FailOverStrategy::FAIL_OPEN, proxy_headers: nil)
13
+ @api_key = api_key
14
+ @api_url = api_url
15
+ @interval = interval
16
+ @max_events = max_events
17
+ @timeout = timeout
18
+ @auto_send = auto_send
19
+ @disable = disable
20
+ @log_level = log_level
21
+ @fail_over_strategy = fail_over_strategy
22
+ @proxy_headers = proxy_headers
23
+ end
24
+
25
+ def self.default_securenative_options
26
+ SecureNativeOptions.new
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'securenative/config/configuration_builder'
5
+
6
+ module SecureNative
7
+ class ConfigurationManager
8
+ DEFAULT_CONFIG_FILE = 'securenative.yml'
9
+ CUSTOM_CONFIG_FILE_ENV_NAME = 'SECURENATIVE_CONFIG_FILE'
10
+ @config = nil
11
+
12
+ def self.read_resource_file(resource_path)
13
+ properties = {}
14
+ begin
15
+ @config = YAML.load_file(resource_path)
16
+ properties = @config unless @config.nil?
17
+ rescue StandardError => e
18
+ SecureNativeLogger.error("Could not parse securenative.config file #{resource_path}; #{e}")
19
+ end
20
+ properties
21
+ end
22
+
23
+ def self._get_resource_path(env_name)
24
+ Env.fetch(env_name, ENV[DEFAULT_CONFIG_FILE])
25
+ end
26
+
27
+ def self.config_builder
28
+ ConfigurationBuilder.new
29
+ end
30
+
31
+ def self._get_env_or_default(properties, key, default)
32
+ return ENV[key] if ENV[key]
33
+ return properties[key] if properties[key]
34
+
35
+ default
36
+ end
37
+
38
+ def self.load_config
39
+ options = ConfigurationBuilder.default_securenative_options
40
+
41
+ resource_path = DEFAULT_CONFIG_FILE
42
+ resource_path = ENV[CUSTOM_CONFIG_FILE_ENV_NAME] unless ENV[CUSTOM_CONFIG_FILE_ENV_NAME].nil?
43
+
44
+ properties = read_resource_file(resource_path)
45
+
46
+ ConfigurationBuilder.new(api_key: _get_env_or_default(properties, 'SECURENATIVE_API_KEY', options.api_key),
47
+ api_url: _get_env_or_default(properties, 'SECURENATIVE_API_URL', options.api_url),
48
+ interval: _get_env_or_default(properties, 'SECURENATIVE_INTERVAL', options.interval),
49
+ max_events: _get_env_or_default(properties, 'SECURENATIVE_MAX_EVENTS', options.max_events),
50
+ timeout: _get_env_or_default(properties, 'SECURENATIVE_TIMEOUT', options.timeout),
51
+ auto_send: _get_env_or_default(properties, 'SECURENATIVE_AUTO_SEND', options.auto_send),
52
+ disable: _get_env_or_default(properties, 'SECURENATIVE_DISABLE', options.disable),
53
+ log_level: _get_env_or_default(properties, 'SECURENATIVE_LOG_LEVEL', options.log_level),
54
+ fail_over_strategy: _get_env_or_default(properties, 'SECURENATIVE_FAILOVER_STRATEGY', options.fail_over_strategy),
55
+ proxy_headers: _get_env_or_default(properties, 'SECURENATIVE_PROXY_HEADERS', options.proxy_headers))
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securenative/enums/failover_strategy'
4
+
5
+ module SecureNative
6
+ class SecureNativeOptions
7
+ attr_reader :api_key, :api_url, :interval, :max_events, :timeout, :auto_send, :disable, :log_level, :fail_over_strategy, :proxy_headers
8
+ attr_writer :api_key, :api_url, :interval, :max_events, :timeout, :auto_send, :disable, :log_level, :fail_over_strategy, :proxy_headers
9
+
10
+ def initialize(api_key: nil, api_url: "https://api.securenative.com/collector/api/v1", interval: 1000,
11
+ max_events: 1000, timeout: 1500, auto_send: true, disable: false, log_level: "FATAL",
12
+ fail_over_strategy: FailOverStrategy::FAIL_OPEN, proxy_headers: nil)
13
+ @api_key = api_key
14
+ @api_url = api_url
15
+ @interval = interval
16
+ @max_events = max_events
17
+ @timeout = timeout
18
+ @auto_send = auto_send
19
+ @disable = disable
20
+ @log_level = log_level
21
+ @fail_over_strategy = fail_over_strategy
22
+ @proxy_headers = proxy_headers
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class HanamiContext
5
+ SECURENATIVE_COOKIE = '_sn'
6
+
7
+ def self.get_client_token(request)
8
+ begin
9
+ request.env[SECURENATIVE_COOKIE]
10
+ rescue StandardError
11
+ begin
12
+ request.cookies[SECURENATIVE_COOKIE]
13
+ rescue StandardError
14
+ nil
15
+ end
16
+ end
17
+ end
18
+
19
+ def self.get_url(request)
20
+ begin
21
+ request.env['REQUEST_PATH']
22
+ rescue StandardError
23
+ nil
24
+ end
25
+ end
26
+
27
+ def self.get_method(request)
28
+ begin
29
+ request.request_method
30
+ rescue StandardError
31
+ nil
32
+ end
33
+ end
34
+
35
+ def self.get_headers(request)
36
+ begin
37
+ # Note: At the moment we're filtering out everything but user-agent since ruby's payload is way too big
38
+ { 'user-agent' => request.env['HTTP_USER_AGENT'] }
39
+ rescue StandardError
40
+ nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class RailsContext
5
+ SECURENATIVE_COOKIE = '_sn'
6
+
7
+ def self.get_client_token(request)
8
+ begin
9
+ request.cookies[SECURENATIVE_COOKIE]
10
+ rescue StandardError
11
+ nil
12
+ end
13
+ end
14
+
15
+ def self.get_url(request)
16
+ begin
17
+ # Rails >= 3.x
18
+ request.fullpath
19
+ rescue StandardError
20
+ begin
21
+ # Rails < 3.x & Sinatra
22
+ request.url if url.nil?
23
+ rescue StandardError
24
+ nil
25
+ end
26
+ end
27
+ end
28
+
29
+ def self.get_method(request)
30
+ begin
31
+ request.method
32
+ rescue StandardError
33
+ nil
34
+ end
35
+ end
36
+
37
+ def self.get_headers(request)
38
+ begin
39
+ # Note: At the moment we're filtering out everything but user-agent since ruby's payload is way too big
40
+ {'user-agent' => request.env['HTTP_USER_AGENT']}
41
+ rescue StandardError
42
+ nil
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securenative/utils/request_utils'
4
+ require 'securenative/utils/utils'
5
+ require 'securenative/context/rails_context'
6
+ require 'securenative/context/hanami_context'
7
+ require 'securenative/context/sinatra_context'
8
+
9
+ module SecureNative
10
+ class SecureNativeContext
11
+ attr_reader :client_token, :ip, :remote_ip, :headers, :url, :http_method, :body
12
+ attr_writer :client_token, :ip, :remote_ip, :headers, :url, :http_method, :body
13
+
14
+ SECURENATIVE_COOKIE = '_sn'
15
+
16
+ def initialize(client_token: '', ip: '', remote_ip: '', headers: nil, url: '', http_method: '', body: '')
17
+ @client_token = client_token
18
+ @ip = ip
19
+ @remote_ip = remote_ip
20
+ @headers = headers
21
+ @url = url
22
+ @http_method = http_method
23
+ @body = body
24
+ end
25
+
26
+ def self.default_context_builder
27
+ SecureNativeContext.new
28
+ end
29
+
30
+ def self.from_http_request(request)
31
+ client_token = RailsContext.get_client_token(request)
32
+ client_token = SinatraContext.get_client_token(request) if client_token.nil?
33
+ client_token = HanamiContext.get_client_token(request) if client_token.nil?
34
+
35
+ begin
36
+ headers = RailsContext.get_headers(request)
37
+ headers = SinatraContext.get_headers(request) if headers.nil?
38
+ headers = HanamiContext.get_headers(request) if headers.nil?
39
+
40
+ # Standard Ruby request
41
+ headers = request.header.to_hash if headers.nil?
42
+ rescue StandardError
43
+ headers = []
44
+ end
45
+
46
+ url = RailsContext.get_url(request)
47
+ url = SinatraContext.get_url(request) if url.nil?
48
+ url = HanamiContext.get_url(request) if url.nil?
49
+ url = '' if url.nil?
50
+
51
+ method = RailsContext.get_method(request)
52
+ method = SinatraContext.get_method(request) if method.nil?
53
+ method = HanamiContext.get_method(request) if method.nil?
54
+ method = '' if method.nil?
55
+
56
+ begin
57
+ body = request.body.to_s
58
+ rescue StandardError
59
+ body = ''
60
+ end
61
+
62
+ client_token = RequestUtils.get_secure_header_from_request(headers) if Utils.null_or_empty?(client_token)
63
+
64
+ SecureNativeContext.new(client_token: client_token, ip: RequestUtils.get_client_ip_from_request(request),
65
+ remote_ip: RequestUtils.get_remote_ip_from_request(request),
66
+ headers: headers, url: url, http_method: method || '', body: body)
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class SinatraContext
5
+ SECURENATIVE_COOKIE = '_sn'
6
+
7
+ def self.get_client_token(request)
8
+ begin
9
+ request.env[SECURENATIVE_COOKIE]
10
+ rescue StandardError
11
+ begin
12
+ request.cookies[SECURENATIVE_COOKIE]
13
+ rescue StandardError
14
+ nil
15
+ end
16
+ end
17
+ end
18
+
19
+ def self.get_url(request)
20
+ begin
21
+ request.env['REQUEST_URI']
22
+ rescue StandardError
23
+ nil
24
+ end
25
+ end
26
+
27
+ def self.get_method(request)
28
+ begin
29
+ request.env['REQUEST_METHOD']
30
+ rescue StandardError
31
+ nil
32
+ end
33
+ end
34
+
35
+ def self.get_headers(request)
36
+ begin
37
+ # Note: At the moment we're filtering out everything but user-agent since ruby's payload is way too big
38
+ {'user-agent' => request.env['HTTP_USER_AGENT']}
39
+ rescue StandardError
40
+ nil
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ module ApiRoute
5
+ TRACK = 'track'
6
+ VERIFY = 'verify'
7
+ end
8
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ module EventTypes
5
+ LOG_IN = 'sn.user.login'
6
+ LOG_IN_CHALLENGE = 'sn.user.login.challenge'
7
+ LOG_IN_FAILURE = 'sn.user.login.failure'
8
+ LOG_OUT = 'sn.user.logout'
9
+ SIGN_UP = 'sn.user.signup'
10
+ AUTH_CHALLENGE = 'sn.user.auth.challenge'
11
+ AUTH_CHALLENGE_SUCCESS = 'sn.user.auth.challenge.success'
12
+ AUTH_CHALLENGE_FAILURE = 'sn.user.auth.challenge.failure'
13
+ TWO_FACTOR_DISABLE = 'sn.user.2fa.disable'
14
+ EMAIL_UPDATE = 'sn.user.email.update'
15
+ PASSWORD_REST = 'sn.user.password.reset'
16
+ PASSWORD_REST_SUCCESS = 'sn.user.password.reset.success'
17
+ PASSWORD_UPDATE = 'sn.user.password.update'
18
+ PASSWORD_REST_FAILURE = 'sn.user.password.reset.failure'
19
+ USER_INVITE = 'sn.user.invite'
20
+ ROLE_UPDATE = 'sn.user.role.update'
21
+ PROFILE_UPDATE = 'sn.user.profile.update'
22
+ PAGE_VIEW = 'sn.user.page.view'
23
+ VERIFY = 'sn.verify'
24
+ end
25
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ module FailOverStrategy
5
+ FAIL_OPEN = 'fail-open'
6
+ FAIL_CLOSED = 'fail-closed'
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ module RiskLevel
5
+ LOW = 'low'
6
+ MEDIUM = 'medium'
7
+ HIGH = 'high'
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class SecureNativeConfigError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class SecureNativeHttpError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class SecureNativeInvalidOptionsError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class SecureNativeInvalidUriError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class SecureNativeParseError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class SecureNativeSDKIllegalStateError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class SecureNativeSDKError < StandardError
5
+ end
6
+ end
@@ -0,0 +1,159 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securenative/utils/secure_native_logger'
4
+ require 'securenative/config/securenative_options'
5
+ require 'securenative/http/securenative_http_client'
6
+ require 'securenative/errors/securenative_sdk_error'
7
+ require 'securenative/errors/securenative_http_error'
8
+
9
+ module SecureNative
10
+ class QueueItem
11
+ attr_reader :url, :body, :retry_sending
12
+ attr_writer :url, :body, :retry_sending
13
+
14
+ def initialize(url, body, retry_sending)
15
+ @url = url
16
+ @body = body
17
+ @retry = retry_sending
18
+ end
19
+ end
20
+
21
+ class EventManager
22
+ def initialize(options = SecureNativeOptions.new, http_client = nil)
23
+ if options.api_key.nil?
24
+ raise SecureNativeSDKError, 'API key cannot be None, please get your API key from SecureNative console.'
25
+ end
26
+
27
+ @http_client = if http_client.nil?
28
+ SecureNativeHttpClient.new(options)
29
+ else
30
+ http_client
31
+ end
32
+
33
+ @queue = []
34
+ @semaphore = Mutex.new
35
+ @interval = options.interval
36
+ @options = options
37
+ @send_enabled = false
38
+ @attempt = 0
39
+ @coefficients = [1, 1, 2, 3, 5, 8, 13]
40
+
41
+ @thread = Thread.new { run }
42
+ end
43
+
44
+ def send_async(event, resource_path)
45
+ if @options.disable
46
+ SecureNativeLogger.warning('SDK is disabled. no operation will be performed')
47
+ return
48
+ end
49
+
50
+ item = QueueItem.new(resource_path, EventManager.serialize(event).to_json, false)
51
+ @queue.append(item)
52
+ end
53
+
54
+ def flush
55
+ @queue.each do |item|
56
+ @http_client.post(item.url, item.body)
57
+ end
58
+ end
59
+
60
+ def send_sync(event, resource_path, retry_sending)
61
+ if @options.disable
62
+ SecureNativeLogger.warning('SDK is disabled. no operation will be performed')
63
+ return
64
+ end
65
+
66
+ SecureNativeLogger.debug("Attempting to send event #{event}")
67
+ res = @http_client.post(resource_path, EventManager.serialize(event).to_json)
68
+
69
+ if res.nil? || res.code != '200'
70
+ SecureNativeLogger.info("SecureNative failed to call endpoint #{resource_path} with event #{event}. adding back to queue")
71
+ item = QueueItem.new(resource_path, EventManager.serialize(event).to_json, retry_sending)
72
+ @queue.append(item)
73
+ end
74
+
75
+ res
76
+ end
77
+
78
+ def run
79
+ loop do
80
+ @semaphore.synchronize do
81
+ next unless !@queue.empty? && @send_enabled
82
+
83
+ @queue.each do |item|
84
+ begin
85
+ res = @http_client.post(item.url, item.body)
86
+ if res.code == '401'
87
+ item.retry_sending = false
88
+ elsif res.code != '200'
89
+ raise SecureNativeHttpError, res.status_code
90
+ end
91
+ SecureNativeLogger.debug("Event successfully sent; #{item.body}")
92
+ return res
93
+ rescue StandardError => e
94
+ SecureNativeLogger.error("Failed to send event; #{e}")
95
+ if item.retry_sending
96
+ @attempt = 0 if @coefficients.length == @attempt + 1
97
+
98
+ back_off = @coefficients[@attempt] * @options.interval
99
+ SecureNativeLogger.debug("Automatic back-off of #{back_off}")
100
+ @send_enabled = false
101
+ sleep back_off
102
+ @send_enabled = true
103
+ end
104
+ end
105
+ end
106
+ end
107
+ sleep @interval / 1000
108
+ end
109
+ end
110
+
111
+ def start_event_persist
112
+ SecureNativeLogger.debug('Starting automatic event persistence')
113
+ if @options.auto_send || @send_enabled
114
+ @send_enabled = true
115
+ else
116
+ SecureNativeLogger.debug('Automatic event persistence is disabled, you should persist events manually')
117
+ end
118
+ end
119
+
120
+ def stop_event_persist
121
+ if @send_enabled
122
+ SecureNativeLogger.debug('Attempting to stop automatic event persistence')
123
+ begin
124
+ flush
125
+ @thread&.stop?
126
+ SecureNativeLogger.debug('Stopped event persistence')
127
+ rescue StandardError => e
128
+ SecureNativeLogger.error("Could not stop event scheduler; #{e}")
129
+ end
130
+ end
131
+ end
132
+
133
+ def self.serialize(obj)
134
+ {
135
+ rid: obj.rid,
136
+ eventType: obj.event_type,
137
+ userId: obj.user_id,
138
+ userTraits: {
139
+ name: obj.user_traits.name,
140
+ email: obj.user_traits.email,
141
+ phone: obj.user_traits.phone,
142
+ createdAt: obj.user_traits.created_at
143
+ },
144
+ request: {
145
+ cid: obj.request.cid,
146
+ vid: obj.request.vid,
147
+ fp: obj.request.fp,
148
+ ip: obj.request.ip,
149
+ remoteIp: obj.request.remote_ip,
150
+ method: obj.request.http_method || '',
151
+ url: obj.request.url,
152
+ headers: obj.request.headers
153
+ },
154
+ timestamp: obj.timestamp,
155
+ properties: obj.properties
156
+ }
157
+ end
158
+ end
159
+ end