securenative 0.1.23 → 0.1.24

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