securenative 0.1.23 → 0.1.24

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 (83) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -5
  3. data/README.md +36 -36
  4. data/lib/securenative/api_manager.rb +41 -0
  5. data/lib/securenative/config/configuration_builder.rb +29 -0
  6. data/lib/securenative/config/configuration_manager.rb +58 -0
  7. data/lib/securenative/config/securenative_options.rb +25 -0
  8. data/lib/securenative/context/hanami_context.rb +44 -0
  9. data/lib/securenative/context/rails_context.rb +46 -0
  10. data/lib/securenative/context/securenative_context.rb +69 -0
  11. data/lib/securenative/context/sinatra_context.rb +44 -0
  12. data/lib/securenative/enums/api_route.rb +8 -0
  13. data/lib/securenative/enums/event_types.rb +25 -0
  14. data/lib/securenative/enums/failover_strategy.rb +8 -0
  15. data/lib/securenative/enums/risk_level.rb +9 -0
  16. data/lib/securenative/errors/securenative_config_error.rb +6 -0
  17. data/lib/securenative/errors/securenative_http_error.rb +6 -0
  18. data/lib/securenative/errors/securenative_invalid_options_error.rb +6 -0
  19. data/lib/securenative/errors/securenative_invalid_uri_error.rb +6 -0
  20. data/lib/securenative/errors/securenative_parse_error.rb +6 -0
  21. data/lib/securenative/errors/securenative_sdk_Illegal_state_error.rb +6 -0
  22. data/lib/securenative/errors/securenative_sdk_error.rb +6 -0
  23. data/lib/securenative/event_manager.rb +159 -0
  24. data/lib/securenative/http/secure_native_http_response.rb +14 -0
  25. data/lib/securenative/http/securenative_http_client.rb +52 -0
  26. data/lib/securenative/models/client_token.rb +14 -0
  27. data/lib/securenative/models/device.rb +12 -0
  28. data/lib/securenative/models/event_options.rb +39 -0
  29. data/lib/securenative/models/request_context.rb +20 -0
  30. data/lib/securenative/models/request_options.rb +14 -0
  31. data/lib/securenative/models/sdk_event.rb +51 -0
  32. data/lib/securenative/models/user_traits.rb +15 -0
  33. data/lib/securenative/models/verify_result.rb +18 -0
  34. data/lib/securenative/sdk.rb +85 -0
  35. data/lib/securenative/utils/date_utils.rb +11 -0
  36. data/lib/securenative/utils/encryption_utils.rb +51 -0
  37. data/lib/securenative/utils/ip_utils.rb +25 -0
  38. data/lib/securenative/utils/request_utils.rb +71 -0
  39. data/lib/securenative/utils/secure_native_logger.rb +46 -0
  40. data/lib/securenative/utils/signature_utils.rb +18 -0
  41. data/lib/securenative/utils/utils.rb +11 -0
  42. data/lib/securenative/utils/version_utils.rb +13 -0
  43. data/securenative.gemspec +16 -14
  44. metadata +40 -40
  45. data/lib/api_manager.rb +0 -39
  46. data/lib/config/configuration_builder.rb +0 -27
  47. data/lib/config/configuration_manager.rb +0 -56
  48. data/lib/config/securenative_options.rb +0 -23
  49. data/lib/context/hanami_context.rb +0 -42
  50. data/lib/context/rails_context.rb +0 -44
  51. data/lib/context/securenative_context.rb +0 -67
  52. data/lib/context/sinatra_context.rb +0 -42
  53. data/lib/enums/api_route.rb +0 -6
  54. data/lib/enums/event_types.rb +0 -23
  55. data/lib/enums/failover_strategy.rb +0 -6
  56. data/lib/enums/risk_level.rb +0 -7
  57. data/lib/errors/securenative_config_error.rb +0 -4
  58. data/lib/errors/securenative_http_error.rb +0 -4
  59. data/lib/errors/securenative_invalid_options_error.rb +0 -4
  60. data/lib/errors/securenative_invalid_uri_error.rb +0 -4
  61. data/lib/errors/securenative_parse_error.rb +0 -4
  62. data/lib/errors/securenative_sdk_Illegal_state_error.rb +0 -4
  63. data/lib/errors/securenative_sdk_error.rb +0 -4
  64. data/lib/event_manager.rb +0 -157
  65. data/lib/http/secure_native_http_response.rb +0 -12
  66. data/lib/http/securenative_http_client.rb +0 -50
  67. data/lib/models/client_token.rb +0 -12
  68. data/lib/models/device.rb +0 -10
  69. data/lib/models/event_options.rb +0 -37
  70. data/lib/models/request_context.rb +0 -18
  71. data/lib/models/request_options.rb +0 -12
  72. data/lib/models/sdk_event.rb +0 -49
  73. data/lib/models/user_traits.rb +0 -13
  74. data/lib/models/verify_result.rb +0 -16
  75. data/lib/securenative.rb +0 -83
  76. data/lib/utils/date_utils.rb +0 -9
  77. data/lib/utils/encryption_utils.rb +0 -49
  78. data/lib/utils/ip_utils.rb +0 -23
  79. data/lib/utils/request_utils.rb +0 -69
  80. data/lib/utils/secure_native_logger.rb +0 -44
  81. data/lib/utils/signature_utils.rb +0 -16
  82. data/lib/utils/utils.rb +0 -9
  83. data/lib/utils/version_utils.rb +0 -11
@@ -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
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class SecureNativeHttpResponse
5
+ attr_reader :ok, :status_code, :body
6
+ attr_writer :ok, :status_code, :body
7
+
8
+ def initialize(ok, status_code, body)
9
+ @ok = ok
10
+ @status_code = status_code
11
+ @body = body
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'net/http'
4
+ require 'uri'
5
+ require 'json'
6
+ require 'securenative/utils/version_utils'
7
+ require 'securenative/utils/secure_native_logger'
8
+
9
+ module SecureNative
10
+ class SecureNativeHttpClient
11
+ AUTHORIZATION_HEADER = 'Authorization'
12
+ VERSION_HEADER = 'SN-Version'
13
+ USER_AGENT_HEADER = 'User-Agent'
14
+ USER_AGENT_HEADER_VALUE = 'SecureNative-ruby'
15
+ CONTENT_TYPE_HEADER = 'Content-Type'
16
+ CONTENT_TYPE_HEADER_VALUE = 'application/json'
17
+
18
+ def initialize(securenative_options)
19
+ @options = securenative_options
20
+ end
21
+
22
+ def _headers
23
+ {
24
+ CONTENT_TYPE_HEADER => CONTENT_TYPE_HEADER_VALUE,
25
+ USER_AGENT_HEADER => USER_AGENT_HEADER_VALUE,
26
+ VERSION_HEADER => VersionUtils.version,
27
+ AUTHORIZATION_HEADER => @options.api_key
28
+ }
29
+ end
30
+
31
+ def post(path, body)
32
+ uri = URI.parse("#{@options.api_url}/#{path}")
33
+ headers = _headers
34
+
35
+ client = Net::HTTP.new(uri.host, uri.port)
36
+ client.use_ssl = true
37
+ client.verify_mode = OpenSSL::SSL::VERIFY_NONE
38
+
39
+ request = Net::HTTP::Post.new(uri.request_uri, headers)
40
+ request.body = body
41
+
42
+ res = nil
43
+ begin
44
+ res = client.request(request)
45
+ rescue StandardError => e
46
+ SecureNativeLogger.error("Failed to send request; #{e}")
47
+ return res
48
+ end
49
+ res
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class ClientToken
5
+ attr_reader :cid, :vid, :fp
6
+ attr_writer :cid, :vid, :fp
7
+
8
+ def initialize(cid, vid, fp)
9
+ @cid = cid
10
+ @vid = vid
11
+ @fp = fp
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class Device
5
+ attr_reader :device_id
6
+ attr_writer :device_id
7
+
8
+ def initialize(device_id)
9
+ @device_id = device_id
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securenative/models/event_options'
4
+ require 'securenative/models/user_traits'
5
+ require 'securenative/errors/securenative_invalid_options_error'
6
+
7
+ module SecureNative
8
+ class EventOptions
9
+ attr_reader :event, :user_id, :user_traits, :context, :properties, :timestamp
10
+ attr_writer :event, :user_id, :user_traits, :context, :properties, :timestamp
11
+
12
+ MAX_PROPERTIES_SIZE = 10
13
+
14
+ def initialize(event: nil, user_id: nil, user_traits: nil, user_name: nil, email: nil, phone: nil, created_at: nil, context: nil, properties: nil, timestamp: nil)
15
+ if !properties.nil? && properties.length > MAX_PROPERTIES_SIZE
16
+ raise SecureNativeInvalidOptionsError, "You can have only up to #{MAX_PROPERTIES_SIZE} custom properties"
17
+ end
18
+
19
+ if user_traits.nil?
20
+ if user_name && email && phone && created_at
21
+ user_traits = UserTraits(user_name, email, phone, created_at)
22
+ elsif user_name && email && phone
23
+ user_traits = UserTraits(user_name, email, phone)
24
+ elsif user_name && email
25
+ user_traits = UserTraits(user_name, email)
26
+ else
27
+ user_traits = UserTraits.new
28
+ end
29
+ end
30
+
31
+ @event = event
32
+ @user_id = user_id
33
+ @user_traits = user_traits
34
+ @context = context
35
+ @properties = properties
36
+ @timestamp = timestamp
37
+ end
38
+ end
39
+ end