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,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class RequestContext
5
+ attr_reader :cid, :vid, :fp, :ip, :remote_ip, :headers, :url, :http_method
6
+ attr_writer :cid, :vid, :fp, :ip, :remote_ip, :headers, :url, :http_method
7
+
8
+ def initialize(cid: nil, vid: nil, fp: nil, ip: nil, remote_ip: nil, headers: nil, url: nil, http_method: nil)
9
+ @cid = cid
10
+ @vid = vid
11
+ @fp = fp
12
+ @ip = ip
13
+ @remote_ip = remote_ip
14
+ @headers = headers
15
+ @url = url
16
+ @method = http_method
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class RequestOptions
5
+ attr_reader :url, :body, :retry_sending
6
+ attr_writer :url, :body, :retry_sending
7
+
8
+ def initialize(url, body, retry_sending)
9
+ @url = url
10
+ @body = body
11
+ @retry_sending = retry_sending
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securenative/context/securenative_context'
4
+ require 'securenative/errors/securenative_invalid_options_error'
5
+ require 'securenative/utils/encryption_utils'
6
+ require 'securenative/utils/date_utils'
7
+ require 'securenative/models/request_context'
8
+ require 'securerandom'
9
+
10
+ module SecureNative
11
+ class SDKEvent
12
+ attr_reader :context, :rid, :event_type, :user_id, :user_traits, :request, :timestamp, :properties
13
+ attr_writer :context, :rid, :event_type, :user_id, :user_traits, :request, :timestamp, :properties
14
+
15
+ def initialize(event_options, securenative_options)
16
+ if event_options.user_id.nil? || event_options.user_id.length <= 0 || event_options.user_id == ''
17
+ raise SecureNativeInvalidOptionsError.new, 'Invalid event structure; User Id is missing'
18
+ end
19
+
20
+ if event_options.event.nil? || event_options.event.length <= 0 || event_options.event == ''
21
+ raise SecureNativeInvalidOptionsError.new, 'Invalid event structure; Event Type is missing'
22
+ end
23
+
24
+ @context = if !event_options.context.nil?
25
+ event_options.context
26
+ else
27
+ SecureNativeContext.default_context_builder
28
+ end
29
+
30
+ client_token = EncryptionUtils.decrypt(@context.client_token, securenative_options.api_key)
31
+
32
+ @rid = SecureRandom.uuid.to_str
33
+ @event_type = event_options.event
34
+ @user_id = event_options.user_id
35
+ @user_traits = event_options.user_traits
36
+ @request = RequestContext.new(cid: client_token ? client_token.cid : '', vid: client_token ? client_token.vid : '',
37
+ fp: client_token ? client_token.fp : '', ip: @context.ip,
38
+ remote_ip: @context.remote_ip, headers: @context.headers,
39
+ url: @context.url, http_method: @context.http_method)
40
+
41
+
42
+ @timestamp = DateUtils.to_timestamp(event_options.timestamp)
43
+ @properties = event_options.properties
44
+ end
45
+
46
+ def to_s
47
+ "securenative.context: #{@context}, rid: #{@rid}, event_type: #{@event_type}, user_id: #{@user_id},
48
+ user_traits: #{@user_traits}, request: #{@request}, timestamp: #{@timestamp}, properties: #{@properties}"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class UserTraits
5
+ attr_reader :name, :email, :phone, :created_at
6
+ attr_writer :name, :email, :phone, :created_at
7
+
8
+ def initialize(name: nil, email: nil, phone: nil, created_at: nil)
9
+ @name = name
10
+ @email = email
11
+ @created_at = created_at
12
+ @phone = phone
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class VerifyResult
5
+ attr_reader :risk_level, :score, :triggers
6
+ attr_writer :risk_level, :score, :triggers
7
+
8
+ def initialize(risk_level: nil, score: nil, triggers: nil)
9
+ @risk_level = risk_level
10
+ @score = score
11
+ @triggers = triggers
12
+ end
13
+
14
+ def to_s
15
+ "risk_level: #{@risk_level}, score: #{@score}, triggers: #{@triggers}"
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'securenative/utils/secure_native_logger'
4
+ require 'securenative/utils/signature_utils'
5
+ require 'securenative/utils/utils'
6
+ require 'securenative/errors/securenative_sdk_error'
7
+ require 'securenative/errors/securenative_sdk_Illegal_state_error'
8
+ require 'securenative/errors/securenative_config_error'
9
+ require 'securenative/enums/failover_strategy'
10
+ require 'securenative/config/configuration_builder'
11
+ require 'securenative/config/configuration_manager'
12
+ require 'securenative/event_manager'
13
+ require 'securenative/api_manager'
14
+
15
+ module SecureNative
16
+ class SecureNative
17
+ attr_reader :options
18
+
19
+ def initialize(options)
20
+ @securenative = nil
21
+ raise SecureNativeSDKError, 'You must pass your SecureNative api key' if Utils.null_or_empty?(options.api_key)
22
+
23
+ @options = options
24
+ @event_manager = EventManager.new(@options)
25
+
26
+ @event_manager.start_event_persist unless @options.api_url.nil?
27
+
28
+ @api_manager = ApiManager.new(@event_manager, @options)
29
+ SecureNativeLogger.init_logger(@options.log_level)
30
+ end
31
+
32
+ def self.init_with_options(options)
33
+ if @securenative.nil?
34
+ @securenative = SecureNative.new(options)
35
+ @securenative
36
+ else
37
+ SecureNativeLogger.debug('This SDK was already initialized.')
38
+ raise SecureNativeSDKError, 'This SDK was already initialized.'
39
+ end
40
+ end
41
+
42
+ def self.init_with_api_key(api_key)
43
+ raise SecureNativeConfigError, 'You must pass your SecureNative api key' if Utils.null_or_empty?(api_key)
44
+
45
+ if @securenative.nil?
46
+ options = ConfigurationBuilder.new(api_key: api_key)
47
+ @securenative = SecureNative.new(options)
48
+ @securenative
49
+ else
50
+ SecureNativeLogger.debug('This SDK was already initialized.')
51
+ raise SecureNativeSDKError, 'This SDK was already initialized.'
52
+ end
53
+ end
54
+
55
+ def self.init
56
+ options = ConfigurationManager.load_config
57
+ init_with_options(options)
58
+ end
59
+
60
+ def self.instance
61
+ raise SecureNativeSDKIllegalStateError if @securenative.nil?
62
+
63
+ @securenative
64
+ end
65
+
66
+ def track(event_options)
67
+ @api_manager.track(event_options)
68
+ end
69
+
70
+ def verify(event_options)
71
+ @api_manager.verify(event_options)
72
+ end
73
+
74
+ def self._flush
75
+ @securenative = nil
76
+ end
77
+
78
+ def verify_request_payload(request)
79
+ request_signature = request.header[SignatureUtils.SIGNATURE_HEADER]
80
+ body = request.body
81
+
82
+ SignatureUtils.valid_signature?(@options.api_key, body, request_signature)
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class DateUtils
5
+ def self.to_timestamp(date)
6
+ return Time.now.utc.iso8601 if date.nil?
7
+
8
+ Time.parse(date).iso8601
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+ require 'digest'
5
+ require 'base64'
6
+ require 'securenative/models/client_token'
7
+
8
+ module SecureNative
9
+ class EncryptionUtils
10
+ def self.padding_key(key, length)
11
+ if key.length == length
12
+ key
13
+ else
14
+ if key.length > length
15
+ key.slice(0, length)
16
+ else
17
+ (length - key.length).times { key << '0' }
18
+ key
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.encrypt(plain_text, secret_key)
24
+ begin
25
+ cipher = OpenSSL::Cipher.new('aes-256-cbc')
26
+ cipher.encrypt
27
+ iv = cipher.random_iv
28
+ cipher.key = padding_key(secret_key, 32)
29
+ encrypted = cipher.update(plain_text) + cipher.final
30
+ (iv + encrypted).unpack1('H*')
31
+ rescue StandardError
32
+ ''
33
+ end
34
+ end
35
+
36
+ def self.decrypt(cipher_text, secret_key)
37
+ begin
38
+ cipher = OpenSSL::Cipher.new('aes-256-cbc')
39
+ cipher.decrypt
40
+ raw_data = [cipher_text].pack('H*')
41
+ cipher.iv = raw_data.slice(0, 16)
42
+ cipher.key = padding_key(secret_key, 32)
43
+ decrypted = JSON.parse(cipher.update(raw_data.slice(16, raw_data.length)) + cipher.final)
44
+
45
+ return ClientToken.new(decrypted['cid'], decrypted['vid'], decrypted['fp'])
46
+ rescue StandardError
47
+ ClientToken.new('', '', '')
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "resolv"
4
+
5
+ module SecureNative
6
+ class IpUtils
7
+ def self.ip_address?(ip_address)
8
+ return true if ip_address =~ Resolv::IPv4::Regex
9
+ return true if ip_address =~ Resolv::IPv6::Regex
10
+
11
+ false
12
+ end
13
+
14
+ def self.valid_public_ip?(ip_address)
15
+ ip = IPAddr.new(ip_address)
16
+ return false if ip.loopback? || ip.private? || ip.link_local? || ip.untrusted? || ip.tainted?
17
+
18
+ true
19
+ end
20
+
21
+ def self.loop_back?(ip_address)
22
+ IPAddr.new(ip_address).loopback?
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class RequestUtils
5
+ SECURENATIVE_COOKIE = '_sn'
6
+ SECURENATIVE_HEADER = 'x-securenative'
7
+
8
+ def self.get_secure_header_from_request(headers)
9
+ begin
10
+ return headers[SECURENATIVE_HEADER] unless headers.nil?
11
+ rescue StandardError
12
+ []
13
+ end
14
+ []
15
+ end
16
+
17
+ def self.get_client_ip_from_request(request, options = nil)
18
+ begin
19
+ return request.ip unless request.ip.nil?
20
+ rescue NoMethodError
21
+ end
22
+
23
+ begin
24
+ x_forwarded_for = request.env['HTTP_X_FORWARDED_FOR']
25
+ return x_forwarded_for.scan(/\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/)[0] unless x_forwarded_for.nil?
26
+ rescue NoMethodError
27
+ begin
28
+ x_forwarded_for = request['HTTP_X_FORWARDED_FOR']
29
+ return x_forwarded_for.scan(/\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/)[0] unless x_forwarded_for.nil?
30
+ rescue NoMethodError
31
+ end
32
+ end
33
+
34
+ begin
35
+ x_forwarded_for = request.env['REMOTE_ADDR']
36
+ return x_forwarded_for.scan(/\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/)[0] unless x_forwarded_for.nil?
37
+ rescue NoMethodError
38
+ begin
39
+ x_forwarded_for = request['REMOTE_ADDR']
40
+ return x_forwarded_for.scan(/\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/)[0] unless x_forwarded_for.nil?
41
+ rescue NoMethodError
42
+ end
43
+ end
44
+
45
+ unless options.nil?
46
+ for header in options.proxy_headers do
47
+ begin
48
+ h = request.env[header]
49
+ return h.scan(/\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/)[0] unless h.nil?
50
+ rescue NoMethodError
51
+ begin
52
+ h = request[header]
53
+ return h.scan(/\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b/)[0] unless h.nil?
54
+ rescue NoMethodError
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ ''
61
+ end
62
+
63
+ def self.get_remote_ip_from_request(request)
64
+ begin
65
+ request.remote_ip
66
+ rescue NoMethodError
67
+ ''
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ module SecureNative
6
+ class SecureNativeLogger
7
+ @logger = Logger.new(STDOUT)
8
+
9
+ def self.init_logger(level = 'DEBUG')
10
+ @logger.level = case level
11
+ when 'WARN'
12
+ Logger::WARN
13
+ when 'DEBUG'
14
+ Logger::DEBUG
15
+ when 'ERROR'
16
+ Logger::ERROR
17
+ when 'FATAL'
18
+ Logger::FATAL
19
+ when 'INFO'
20
+ Logger::INFO
21
+ else
22
+ Logger::FATAL
23
+ end
24
+
25
+ @logger.formatter = proc do |severity, datetime, progname, msg|
26
+ "[#{datetime}] #{severity} (#{progname}): #{msg}\n"
27
+ end
28
+ end
29
+
30
+ def self.info(msg)
31
+ @logger.info(msg)
32
+ end
33
+
34
+ def self.debug(msg)
35
+ @logger.debug(msg)
36
+ end
37
+
38
+ def self.warning(msg)
39
+ @logger.warning(msg)
40
+ end
41
+
42
+ def self.error(msg)
43
+ @logger.error(msg)
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ module SecureNative
6
+ class SignatureUtils
7
+ SIGNATURE_HEADER = 'x-securenative'
8
+
9
+ def self.valid_signature?(api_key, payload, header_signature)
10
+ key = api_key.encode('utf-8')
11
+ body = payload.encode('utf-8')
12
+ calculated_signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha512'), key, body)
13
+ calculated_signature.eql? header_signature
14
+ rescue StandardError
15
+ false
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SecureNative
4
+ class Utils
5
+ def self.null_or_empty?(string)
6
+ return true if !string || string.empty? || string.nil?
7
+
8
+ false
9
+ end
10
+ end
11
+ end