securenative 0.1.5 → 0.1.21

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 (108) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +49 -0
  3. data/.github/workflows/publish.yml +60 -0
  4. data/.github/workflows/test.yml +48 -0
  5. data/.gitignore +3 -1
  6. data/.rakeTasks +7 -0
  7. data/.rspec +3 -0
  8. data/Gemfile +6 -1
  9. data/Gemfile.lock +254 -17
  10. data/README.md +140 -67
  11. data/Rakefile +5 -1
  12. data/lib/api_manager.rb +39 -0
  13. data/lib/config/configuration_builder.rb +26 -0
  14. data/lib/config/configuration_manager.rb +55 -0
  15. data/lib/config/securenative_options.rb +22 -0
  16. data/lib/context/hanami_context.rb +42 -0
  17. data/lib/context/rails_context.rb +44 -0
  18. data/lib/context/securenative_context.rb +67 -0
  19. data/lib/context/sinatra_context.rb +42 -0
  20. data/lib/enums/api_route.rb +6 -0
  21. data/lib/enums/event_types.rb +23 -0
  22. data/lib/enums/failover_strategy.rb +6 -0
  23. data/lib/enums/risk_level.rb +7 -0
  24. data/lib/errors/securenative_config_error.rb +4 -0
  25. data/lib/errors/securenative_http_error.rb +4 -0
  26. data/lib/errors/securenative_invalid_options_error.rb +4 -0
  27. data/lib/errors/securenative_invalid_uri_error.rb +4 -0
  28. data/lib/errors/securenative_parse_error.rb +4 -0
  29. data/lib/errors/securenative_sdk_Illegal_state_error.rb +4 -0
  30. data/lib/errors/securenative_sdk_error.rb +4 -0
  31. data/lib/event_manager.rb +157 -0
  32. data/lib/http/secure_native_http_response.rb +12 -0
  33. data/lib/http/securenative_http_client.rb +50 -0
  34. data/lib/models/client_token.rb +12 -0
  35. data/lib/models/device.rb +10 -0
  36. data/lib/models/event_options.rb +37 -0
  37. data/lib/models/request_context.rb +18 -0
  38. data/lib/models/request_options.rb +12 -0
  39. data/lib/models/sdk_event.rb +40 -0
  40. data/lib/models/user_traits.rb +13 -0
  41. data/lib/models/verify_result.rb +16 -0
  42. data/lib/securenative.rb +69 -25
  43. data/lib/utils/date_utils.rb +9 -0
  44. data/lib/utils/encryption_utils.rb +49 -0
  45. data/lib/utils/ip_utils.rb +23 -0
  46. data/lib/utils/request_utils.rb +54 -0
  47. data/lib/utils/secure_native_logger.rb +44 -0
  48. data/lib/utils/signature_utils.rb +16 -0
  49. data/lib/utils/utils.rb +9 -0
  50. data/lib/utils/version_utils.rb +11 -0
  51. data/out/production/securenative-ruby/api_manager.rb +31 -0
  52. data/out/production/securenative-ruby/config/configuration_builder.rb +30 -0
  53. data/out/production/securenative-ruby/config/configuration_manager.rb +55 -0
  54. data/out/production/securenative-ruby/config/securenative_options.rb +20 -0
  55. data/out/production/securenative-ruby/context/securenative_context.rb +40 -0
  56. data/out/production/securenative-ruby/enums/api_route.rb +6 -0
  57. data/out/production/securenative-ruby/enums/event_types.rb +23 -0
  58. data/out/production/securenative-ruby/enums/failover_strategy.rb +6 -0
  59. data/out/production/securenative-ruby/enums/risk_level.rb +7 -0
  60. data/out/production/securenative-ruby/errors/securenative_config_error.rb +4 -0
  61. data/out/production/securenative-ruby/errors/securenative_http_error.rb +4 -0
  62. data/out/production/securenative-ruby/errors/securenative_invalid_options_error.rb +4 -0
  63. data/out/production/securenative-ruby/errors/securenative_invalid_uri_error.rb +4 -0
  64. data/out/production/securenative-ruby/errors/securenative_parse_error.rb +4 -0
  65. data/out/production/securenative-ruby/errors/securenative_sdk_Illegal_state_error.rb +4 -0
  66. data/out/production/securenative-ruby/errors/securenative_sdk_error.rb +4 -0
  67. data/out/production/securenative-ruby/event_manager.rb +156 -0
  68. data/out/production/securenative-ruby/event_options.rb +32 -0
  69. data/out/production/securenative-ruby/http/http_response.rb +12 -0
  70. data/out/production/securenative-ruby/http/securenative_http_client.rb +32 -0
  71. data/out/production/securenative-ruby/models/client_token.rb +12 -0
  72. data/out/production/securenative-ruby/models/device.rb +10 -0
  73. data/out/production/securenative-ruby/models/event_options.rb +15 -0
  74. data/out/production/securenative-ruby/models/request_context.rb +18 -0
  75. data/out/production/securenative-ruby/models/request_options.rb +12 -0
  76. data/out/production/securenative-ruby/models/sdk_event.rb +33 -0
  77. data/out/production/securenative-ruby/models/user_traits.rb +13 -0
  78. data/out/production/securenative-ruby/models/verify_result.rb +12 -0
  79. data/out/production/securenative-ruby/securenative.rb +91 -0
  80. data/out/production/securenative-ruby/utils/date_utils.rb +9 -0
  81. data/out/production/securenative-ruby/utils/encryption_utils.rb +35 -0
  82. data/out/production/securenative-ruby/utils/ip_utils.rb +23 -0
  83. data/out/production/securenative-ruby/utils/request_utils.rb +23 -0
  84. data/out/production/securenative-ruby/utils/secure_native_logger.rb +44 -0
  85. data/out/production/securenative-ruby/utils/signature_utils.rb +16 -0
  86. data/out/production/securenative-ruby/utils/utils.rb +9 -0
  87. data/out/production/securenative-ruby/utils/version_utils.rb +12 -0
  88. data/out/test/securenative-ruby/spec_api_manager.rb +81 -0
  89. data/out/test/securenative-ruby/spec_context_builder.rb +69 -0
  90. data/out/test/securenative-ruby/spec_date_utils.rb +13 -0
  91. data/out/test/securenative-ruby/spec_encryption_utils.rb +26 -0
  92. data/out/test/securenative-ruby/spec_event_manager.rb +59 -0
  93. data/out/test/securenative-ruby/spec_helper.rb +20 -0
  94. data/out/test/securenative-ruby/spec_ip_utils.rb +41 -0
  95. data/out/test/securenative-ruby/spec_securenative.rb +65 -0
  96. data/out/test/securenative-ruby/spec_securenative_http_client.rb +23 -0
  97. data/out/test/securenative-ruby/spec_signature_utils.rb +18 -0
  98. data/securenative.gemspec +4 -4
  99. metadata +96 -15
  100. data/lib/securenative/config.rb +0 -9
  101. data/lib/securenative/event_manager.rb +0 -88
  102. data/lib/securenative/event_options.rb +0 -86
  103. data/lib/securenative/event_type.rb +0 -21
  104. data/lib/securenative/http_client.rb +0 -20
  105. data/lib/securenative/secure_native_sdk.rb +0 -62
  106. data/lib/securenative/securenative_options.rb +0 -17
  107. data/lib/securenative/sn_exception.rb +0 -5
  108. data/lib/securenative/utils.rb +0 -41
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EventOptions
4
+ MAX_PROPERTIES_SIZE = 10
5
+
6
+ def initialize(event_type, user_id, user_traits, user_name, email, phone, created_at, context, properties, timestamp)
7
+ traits = UserTraits(user_name)
8
+ if user_name && email && phone && created_at
9
+ traits = UserTraits(user_name, email, phone, created_at)
10
+ elsif user_name && email && phone
11
+ traits = UserTraits(user_name, email, phone)
12
+ elsif user_name && email
13
+ traits = UserTraits(user_name, email)
14
+ end
15
+
16
+ @event_options = EventOptions(event_type)
17
+ @event_options.user_id = user_id
18
+ @event_options.user_traits = user_traits if user_traits
19
+ @event_options.user_traits = traits
20
+ @event_options.context = context
21
+ @event_options.properties = properties
22
+ @event_options.timestamp = timestamp
23
+ end
24
+
25
+ def build
26
+ if !@event_options.properties.nil? && @event_options.properties.length > MAX_PROPERTIES_SIZE
27
+ raise SecureNativeInvalidOptionsError('You can have only up to {} custom properties', MAX_PROPERTIES_SIZE)
28
+ end
29
+
30
+ @event_options
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HttpResponse
4
+ attr_reader :ok, :status_code, :body
5
+ attr_writer :ok, :status_code, :body
6
+
7
+ def initialize(ok, status_code, body)
8
+ @ok = ok
9
+ @status_code = status_code
10
+ @body = body
11
+ end
12
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'httpclient'
4
+
5
+ class SecureNativeHttpClient
6
+ AUTHORIZATION_HEADER = 'Authorization'
7
+ VERSION_HEADER = 'SN-Version'
8
+ USER_AGENT_HEADER = 'User-Agent'
9
+ USER_AGENT_HEADER_VALUE = 'SecureNative-python'
10
+ CONTENT_TYPE_HEADER = 'Content-Type'
11
+ CONTENT_TYPE_HEADER_VALUE = 'application/json'
12
+
13
+ def initialize(securenative_options)
14
+ @options = securenative_options
15
+ @client = HTTPClient.new
16
+ end
17
+
18
+ def _headers
19
+ {
20
+ CONTENT_TYPE_HEADER => CONTENT_TYPE_HEADER_VALUE,
21
+ USER_AGENT_HEADER => USER_AGENT_HEADER_VALUE,
22
+ VERSION_HEADER => VersionUtils.version,
23
+ AUTHORIZATION_HEADER => @options.api_key
24
+ }
25
+ end
26
+
27
+ def post(path, body)
28
+ url = "#{@options.api_url}/#{path}"
29
+ headers = _headers
30
+ @client.post(url, body, headers)
31
+ end
32
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ClientToken
4
+ attr_reader :cid, :vid, :fp
5
+ attr_writer :cid, :vid, :fp
6
+
7
+ def initialize(cid, vid, fp)
8
+ @cid = cid
9
+ @vid = vid
10
+ @fp = fp
11
+ end
12
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Device
4
+ attr_reader :device_id
5
+ attr_writer :device_id
6
+
7
+ def initialize(device_id)
8
+ @device_id = device_id
9
+ end
10
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ class EventOptions
4
+ attr_reader :event, :user_id, :user_traits, :context, :properties, :timestamp
5
+ attr_writer :event, :user_id, :user_traits, :context, :properties, :timestamp
6
+
7
+ def initialize(event, user_id = nil, user_traits = nil, context = nil, properties = nil, timestamp = nil)
8
+ @event = event
9
+ @user_id = user_id
10
+ @user_traits = user_traits
11
+ @context = context
12
+ @properties = properties
13
+ @timestamp = timestamp
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RequestContext
4
+ attr_reader :cid, :vid, :fp, :ip, :remote_ip, :headers, :url, :http_method
5
+ attr_writer :cid, :vid, :fp, :ip, :remote_ip, :headers, :url, :http_method
6
+
7
+ def initialize(cid = nil, vid = nil, fp = nil, ip = nil, remote_ip = nil, headers = nil, url = nil, method = nil)
8
+ @cid = cid
9
+ @vid = vid
10
+ @fp = fp
11
+ @ip = ip
12
+ @remote_ip = remote_ip
13
+ @headers = headers
14
+ @url = url
15
+ @method = method
16
+ end
17
+ end
18
+
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RequestOptions
4
+ attr_reader :url, :body, :retry_sending
5
+ attr_writer :url, :body, :retry_sending
6
+
7
+ def initialize(url, body, retry_sending)
8
+ @url = url
9
+ @body = body
10
+ @retry_sending = retry_sending
11
+ end
12
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ class SDKEvent
4
+ attr_reader :context, :rid, :event_type, :user_id, :user_traits, :request, :timestamp, :properties
5
+ attr_writer :context, :rid, :event_type, :user_id, :user_traits, :request, :timestamp, :properties
6
+
7
+ def initialize(event_options, securenative_options)
8
+ @context = if !event_options.context.nil?
9
+ event_options.context
10
+ else
11
+ ContextBuilder.default_context_builder
12
+ end
13
+
14
+ client_token = EncryptionUtils.decrypt(@context.client_token, securenative_options.api_key)
15
+
16
+ @rid = SecureRandom.uuid.to_str
17
+ @event_type = event_options.event
18
+ @user_id = event_options.user_id
19
+ @user_traits = event_options.user_traits
20
+ @request = RequestContext(cid = client_token ? client_token.cid : '', vid = client_token ? client_token.vid : '',
21
+ fp = client_token ? client_token.fp : '', ip = @context.ip,
22
+ remote_ip = @context.remote_ip, method = @context.http_method, url = @context.url,
23
+ headers = @context.headers)
24
+
25
+ @timestamp = DateUtils.to_timestamp(event_options.timestamp)
26
+ @properties = event_options.properties
27
+ end
28
+
29
+ def to_s
30
+ "context: #{@context}, rid: #{@rid}, event_type: #{@event_type}, user_id: #{@user_id},
31
+ user_traits: #{@user_traits}, request: #{@request}, timestamp: #{@timestamp}, properties: #{@properties}"
32
+ end
33
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ class UserTraits
4
+ attr_reader :name, :email, :phone, :created_at
5
+ attr_writer :name, :email, :phone, :created_at
6
+
7
+ def initialize(name = nil, email = nil, phone = nil, created_at = nil)
8
+ @name = name
9
+ @email = email
10
+ @created_at = created_at
11
+ @phone = phone
12
+ end
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ class VerifyResult
4
+ attr_reader :risk_level, :score, :triggers
5
+ attr_writer :risk_level, :score, :triggers
6
+
7
+ def initialize(risk_level = nil, score = nil, triggers = nil)
8
+ @risk_level = risk_level
9
+ @score = score
10
+ @triggers = triggers
11
+ end
12
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'utils/secure_native_logger'
4
+ require 'utils/signature_utils'
5
+ require 'utils/utils'
6
+ require 'errors/securenative_sdk_error'
7
+ require 'errors/securenative_sdk_Illegal_state_error'
8
+ require 'errors/securenative_config_error'
9
+ require 'enums/failover_strategy'
10
+ require 'config/configuration_builder'
11
+ require 'event_manager'
12
+
13
+ class SecureNative
14
+ attr_reader :options
15
+
16
+ def initialize(options)
17
+ @securenative = nil
18
+ raise SecureNativeSDKError, 'You must pass your SecureNative api key' if Utils.null_or_empty?(options.api_key)
19
+
20
+ @options = options
21
+ @event_manager = EventManager.new(@options)
22
+
23
+ @event_manager.start_event_persist unless @options.api_url.nil?
24
+
25
+ @api_manager = ApiManager.new(@event_manager, @options)
26
+ SecureNativeLogger.init_logger(@options.log_level)
27
+ end
28
+
29
+ def self.init_with_options(options)
30
+ if @securenative.nil?
31
+ @securenative = SecureNative.new(options)
32
+ @securenative
33
+ else
34
+ SecureNativeLogger.debug('This SDK was already initialized.')
35
+ raise SecureNativeSDKError, 'This SDK was already initialized.'
36
+ end
37
+ end
38
+
39
+ def self.init_with_api_key(api_key)
40
+ raise SecureNativeConfigError, 'You must pass your SecureNative api key' if Utils.null_or_empty?(api_key)
41
+
42
+ if @securenative.nil?
43
+ options = ConfigurationBuilder.new(api_key: api_key)
44
+ @securenative = SecureNative.new(options)
45
+ @securenative
46
+ else
47
+ SecureNativeLogger.debug('This SDK was already initialized.')
48
+ raise SecureNativeSDKError, 'This SDK was already initialized.'
49
+ end
50
+ end
51
+
52
+ def self.init
53
+ options = ConfigurationManager.load_config
54
+ init_with_options(options)
55
+ end
56
+
57
+ def self.instance
58
+ raise SecureNativeSDKIllegalStateError if @securenative.nil?
59
+
60
+ @securenative
61
+ end
62
+
63
+ def self.config_builder(api_key = nil, api_url = 'https://api.securenative.com/collector/api/v1', interval = 1000,
64
+ max_events = 1000, timeout = 1500, auto_send = true, disable = false, log_level = 'FATAL',
65
+ fail_over_strategy = FailOverStrategy::FAIL_OPEN)
66
+ ConfigurationBuilder.new(api_key, api_url, interval, max_events, timeout, auto_send, disable, log_level, fail_over_strategy)
67
+ end
68
+
69
+ def self.context_builder(client_token = nil, ip = nil, remote_ip = nil, headers = nil, url = nil, method = nil, body = nil)
70
+ ContextBuilder.new(client_token, ip, remote_ip, headers, url, method, body)
71
+ end
72
+
73
+ def track(event_options)
74
+ @api_manager.track(event_options)
75
+ end
76
+
77
+ def verify(event_options)
78
+ @api_manager.verify(event_options)
79
+ end
80
+
81
+ def self._flush
82
+ @securenative = nil
83
+ end
84
+
85
+ def verify_request_payload(request)
86
+ request_signature = request.header[SignatureUtils.SIGNATURE_HEADER]
87
+ body = request.body
88
+
89
+ SignatureUtils.valid_signature?(@options.api_key, body, request_signature)
90
+ end
91
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DateUtils
4
+ def self.to_timestamp(date)
5
+ return Time.now.strftime('%Y-%m-%dT%H:%M:%S%Z') if date.nil?
6
+
7
+ Time.parse(date).iso8601
8
+ end
9
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'openssl'
4
+
5
+ class EncryptionUtils
6
+ BLOCK_SIZE = 16
7
+ KEY_SIZE = 32
8
+
9
+ def self.encrypt(text, cipher_key)
10
+ cipher = OpenSSL::Cipher::AES.new(KEY_SIZE, :CBC).encrypt
11
+ cipher.padding = 0
12
+
13
+ if text.size % BLOCK_SIZE != 0
14
+ return nil
15
+ end
16
+
17
+ cipher_key = Digest::SHA1.hexdigest cipher_key
18
+ cipher.key = cipher_key.slice(0, BLOCK_SIZE)
19
+ s = cipher.update(text) + cipher.final
20
+
21
+ s.unpack('H*')[0].upcase
22
+ end
23
+
24
+ def self.decrypt(encrypted, cipher_key)
25
+ cipher = OpenSSL::Cipher::AES.new(KEY_SIZE, :CBC).decrypt
26
+ cipher.padding = 0
27
+
28
+ cipher_key = Digest::SHA1.hexdigest cipher_key
29
+ cipher.key = cipher_key.slice(0, BLOCK_SIZE)
30
+ s = [encrypted].pack('H*').unpack('C*').pack('c*')
31
+
32
+ rv = cipher.update(s) + cipher.final
33
+ rv.strip
34
+ end
35
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "resolv"
4
+
5
+ class IpUtils
6
+ def self.ip_address?(ip_address)
7
+ return true if ip_address =~ Resolv::IPv4::Regex
8
+ return true if ip_address =~ Resolv::IPv6::Regex
9
+
10
+ false
11
+ end
12
+
13
+ def self.valid_public_ip?(ip_address)
14
+ ip = IPAddr.new(ip_address)
15
+ return false if ip.loopback? || ip.private? || ip.link_local? || ip.untrusted? || ip.tainted?
16
+
17
+ true
18
+ end
19
+
20
+ def self.loop_back?(ip_address)
21
+ IPAddr.new(ip_address).loopback?
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RequestUtils
4
+ SECURENATIVE_COOKIE = '_sn'
5
+ SECURENATIVE_HEADER = 'x-securenative'
6
+
7
+ def self.get_secure_header_from_request(headers)
8
+ return headers[RequestUtils.SECURENATIVE_HEADER] unless headers.nil?
9
+
10
+ []
11
+ end
12
+
13
+ def self.get_client_ip_from_request(request)
14
+ x_forwarded_for = request.env['HTTP_X_FORWARDED_FOR']
15
+ return x_forwarded_for unless x_forwarded_for.nil?
16
+
17
+ request.env['REMOTE_ADDR']
18
+ end
19
+
20
+ def self.get_remote_ip_from_request(request)
21
+ request.remote_ip
22
+ end
23
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ class SecureNativeLogger
6
+ @logger = Logger.new(STDOUT)
7
+
8
+ def self.init_logger(level = 'DEBUG')
9
+ @logger.level = case level
10
+ when 'WARN'
11
+ SecureNativeLogger::WARN
12
+ when 'DEBUG'
13
+ SecureNativeLogger::DEBUG
14
+ when 'ERROR'
15
+ SecureNativeLogger::ERROR
16
+ when 'FATAL'
17
+ SecureNativeLogger::FATAL
18
+ when 'INFO'
19
+ SecureNativeLogger::INFO
20
+ else
21
+ SecureNativeLogger::FATAL
22
+ end
23
+
24
+ @logger.formatter = proc do |severity, datetime, progname, msg|
25
+ "[#{datetime}] #{severity} (#{progname}): #{msg}\n"
26
+ end
27
+ end
28
+
29
+ def self.info(msg)
30
+ @logger.info(msg)
31
+ end
32
+
33
+ def self.debug(msg)
34
+ @logger.debug(msg)
35
+ end
36
+
37
+ def self.warning(msg)
38
+ @logger.warning(msg)
39
+ end
40
+
41
+ def self.error(msg)
42
+ @logger.error(msg)
43
+ end
44
+ end