evervault 2.0.0 → 3.0.1

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.
@@ -1,33 +1,26 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Evervault
2
4
  module Errors
3
5
  class EvervaultError < StandardError; end
4
6
 
5
- class ArgumentError < EvervaultError; end
6
-
7
- class HttpError < EvervaultError; end
8
-
9
- class ResourceNotFoundError < EvervaultError; end
10
-
11
- class AuthenticationError < EvervaultError; end
12
-
13
- class ServerError < EvervaultError; end
14
-
15
- class BadGatewayError < EvervaultError; end
16
-
17
- class ServiceUnavailableError < EvervaultError; end
18
-
19
- class BadRequestError < EvervaultError; end
20
-
21
- class UndefinedDataError < EvervaultError; end
7
+ class FunctionError < EvervaultError; end
22
8
 
23
- class InvalidPublicKeyError < EvervaultError; end
9
+ class ForbiddenIPError < FunctionError; end
24
10
 
25
- class UnexpectedError < EvervaultError; end
11
+ class FunctionTimeoutError < FunctionError; end
26
12
 
27
- class CertDownloadError < EvervaultError; end
13
+ class FunctionNotReadyError < FunctionError; end
28
14
 
29
- class UnsupportedEncryptType < EvervaultError; end
15
+ class FunctionRuntimeError < FunctionError
16
+ attr_reader :message, :stack, :id
30
17
 
31
- class ForbiddenIPError < EvervaultError; end
18
+ def initialize(message, stack, id)
19
+ @message = message
20
+ @stack = stack
21
+ @id = id
22
+ super(message.to_s)
23
+ end
24
+ end
32
25
  end
33
26
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'errors'
4
+
5
+ module Evervault
6
+ module Errors
7
+ class LegacyErrorMap
8
+ def self.raise_errors_on_failure(status_code, body, headers)
9
+ return if status_code < 400
10
+
11
+ case status_code
12
+ when 404
13
+ raise EvervaultError, 'Resource not found'
14
+ when 400
15
+ raise EvervaultError, 'Bad request'
16
+ when 401
17
+ raise EvervaultError, 'Unauthorized'
18
+ when 403
19
+ if (headers.include? 'x-evervault-error-code') && (headers['x-evervault-error-code'] == 'forbidden-ip-error')
20
+ raise ForbiddenIPError, 'IP is not present in Cage whitelist'
21
+ end
22
+
23
+ raise EvervaultError, 'Forbidden'
24
+
25
+ when 500
26
+ raise EvervaultError, 'Server error'
27
+ when 502
28
+ raise EvervaultError, 'Bad gateway error'
29
+ when 503
30
+ raise EvervaultError, 'Service unavailable'
31
+ else
32
+ raise EvervaultError, message_for_unexpected_error_without_type(body)
33
+ end
34
+ end
35
+
36
+ private
37
+
38
+ def message_for_unexpected_error_without_type(error_details)
39
+ if error_details.nil?
40
+ return(
41
+ 'An unexpected error occurred without message or status code. Please contact Evervault support'
42
+ )
43
+ end
44
+ message = error_details['message']
45
+ status_code = error_details['statusCode']
46
+ "An unexpected error occured. It occurred with the message: #{
47
+ message
48
+ } and http_code: '#{status_code}'. Please contact Evervault support"
49
+ end
50
+ end
51
+ end
52
+ end
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Evervault
2
4
  module Http
3
5
  class RelayOutboundConfig
4
6
  DEFAULT_POLL_INTERVAL = 5
5
- RELAY_OUTBOUND_CONFIG_API_ENDPOINT = "v2/relay-outbound"
7
+ RELAY_OUTBOUND_CONFIG_API_ENDPOINT = 'v2/relay-outbound'
6
8
 
7
9
  @@destination_domains_cache = nil
8
10
  @@poll_interval = DEFAULT_POLL_INTERVAL
@@ -11,12 +13,10 @@ module Evervault
11
13
  def initialize(base_url:, request:)
12
14
  @base_url = base_url
13
15
  @request = request
14
- if @@destination_domains_cache.nil?
15
- get_relay_outbound_config
16
- end
17
- if @@timer.nil?
18
- @@timer = Evervault::Threading::RepeatedTimer.new(@@poll_interval, -> { get_relay_outbound_config })
19
- end
16
+ get_relay_outbound_config if @@destination_domains_cache.nil?
17
+ return unless @@timer.nil?
18
+
19
+ @@timer = Evervault::Threading::RepeatedTimer.new(@@poll_interval, -> { get_relay_outbound_config })
20
20
  end
21
21
 
22
22
  def get_destination_domains
@@ -24,32 +24,34 @@ module Evervault
24
24
  end
25
25
 
26
26
  def self.disable_polling
27
- unless @@timer.nil?
28
- @@timer.stop
29
- @@timer = nil
30
- end
27
+ return if @@timer.nil?
28
+
29
+ @@timer.stop
30
+ @@timer = nil
31
31
  end
32
32
 
33
33
  def self.clear_cache
34
34
  @@destination_domains_cache = nil
35
35
  end
36
36
 
37
- private def get_relay_outbound_config
38
- resp = @request.execute(:get, "#{@base_url}#{RELAY_OUTBOUND_CONFIG_API_ENDPOINT}", nil)
39
- poll_interval = resp.headers["x-poll-interval"]
40
- unless poll_interval.nil?
41
- update_poll_interval(poll_interval.to_f)
42
- end
37
+ private
38
+
39
+ def get_relay_outbound_config
40
+ resp = @request.execute(:get, "#{@base_url}#{RELAY_OUTBOUND_CONFIG_API_ENDPOINT}")
41
+ poll_interval = resp.headers['x-poll-interval']
42
+ update_poll_interval(poll_interval.to_f) unless poll_interval.nil?
43
43
  resp_body = JSON.parse(resp.body)
44
- @@destination_domains_cache = resp_body["outboundDestinations"].values.map{ |outbound_destination| outbound_destination["destinationDomain"] }
44
+ @@destination_domains_cache = resp_body['outboundDestinations'].values.map do |outbound_destination|
45
+ outbound_destination['destinationDomain']
46
+ end
45
47
  end
46
48
 
47
- private def update_poll_interval(poll_interval)
49
+ def update_poll_interval(poll_interval)
48
50
  @@poll_interval = poll_interval
49
- unless @@timer.nil?
50
- @@timer.update_interval(poll_interval)
51
- end
51
+ return if @@timer.nil?
52
+
53
+ @@timer.update_interval(poll_interval)
52
54
  end
53
55
  end
54
56
  end
55
- end
57
+ end
@@ -1,55 +1,50 @@
1
- require "faraday"
2
- require "json"
3
- require_relative "../version"
4
- require_relative "../errors/error_map"
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'json'
5
+ require_relative '../version'
6
+ require_relative '../errors/legacy_error_map'
5
7
 
6
8
  module Evervault
7
9
  module Http
8
10
  class Request
9
- def initialize(timeout:, app_uuid:, api_key:)
10
- @timeout = timeout
11
- @app_uuid = app_uuid
12
- @api_key = api_key
11
+ attr_reader :config
12
+
13
+ def initialize(config:)
14
+ @config = config
13
15
  end
14
16
 
15
- def execute(method, url, body = nil, optional_headers = {}, basic_auth = false)
16
- resp = faraday(basic_auth).public_send(method, url) do |req, url|
17
- req.body = body.nil? || body.empty? ? nil : body.to_json
18
- req.headers = build_headers(optional_headers, basic_auth)
19
- req.options.timeout = @timeout
17
+ def execute(method, url, body = nil, basic_auth = false, error_map = Evervault::Errors::LegacyErrorMap)
18
+ resp = faraday(basic_auth).public_send(method, url) do |req, _url|
19
+ req.body = body.nil? || body.empty? ? nil : body.to_json
20
+ req.headers = build_headers(basic_auth)
21
+ req.options.timeout = config.request_timeout
20
22
  end
21
23
 
22
- if resp.status >= 200 && resp.status <= 300
23
- return resp
24
- end
24
+ return resp if resp.status >= 200 && resp.status <= 300
25
25
 
26
- Evervault::Errors::ErrorMap.raise_errors_on_failure(resp.status, resp.body, resp.headers)
26
+ error_map.raise_errors_on_failure(resp.status, resp.body, resp.headers)
27
27
  end
28
28
 
29
29
  private
30
30
 
31
31
  def faraday(basic_auth = false)
32
32
  Faraday.new do |conn|
33
- if basic_auth
34
- conn.request :authorization, :basic, @app_uuid, @api_key
35
- end
33
+ conn.request :authorization, :basic, config.app_id, config.api_key if basic_auth
36
34
  end
37
35
  end
38
36
 
39
- def build_headers(optional_headers = {}, basic_auth = false)
37
+ def build_headers(basic_auth = false)
40
38
  headers = {
41
- "AcceptEncoding": "gzip, deflate",
42
- "Accept": "application/json",
43
- "Content-Type": "application/json",
44
- "User-Agent": "evervault-ruby/#{VERSION}",
39
+ "AcceptEncoding": 'gzip, deflate',
40
+ "Accept": 'application/json',
41
+ "Content-Type": 'application/json',
42
+ "User-Agent": "evervault-ruby/#{VERSION}"
45
43
  }
46
- unless optional_headers.nil? || optional_headers.empty?
47
- headers = headers.merge(optional_headers)
48
- end
49
- if !basic_auth
44
+ unless basic_auth
50
45
  headers = headers.merge({
51
- "Api-Key": @api_key,
52
- })
46
+ "Api-Key": config.api_key
47
+ })
53
48
  end
54
49
  headers
55
50
  end
@@ -1,56 +1,53 @@
1
- require "faraday"
2
- require "json"
3
- require_relative "../version"
4
- require_relative "../errors/error_map"
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'json'
5
+ require_relative '../version'
6
+ require_relative '../errors/error_map'
5
7
 
6
8
  module Evervault
7
9
  module Http
8
10
  class RequestHandler
9
- def initialize(request:, base_url:, cert:)
11
+ attr_reader :config
12
+
13
+ def initialize(request:, config:, cert:)
10
14
  @request = request
11
- @base_url = base_url
15
+ @config = config
12
16
  @cert = cert
13
17
  end
14
18
 
15
19
  def get(path)
16
- if @cert.is_certificate_expired()
17
- @cert.setup()
18
- end
20
+ @cert.setup if @cert.is_certificate_expired
19
21
  resp = @request.execute(:get, build_url(path))
20
22
  parse_json_body(resp.body)
21
23
  end
22
24
 
23
25
  def put(path, body)
24
- if @cert.is_certificate_expired()
25
- @cert.setup()
26
- end
26
+ @cert.setup if @cert.is_certificate_expired
27
27
  resp = @request.execute(:put, build_url(path), body)
28
28
  parse_json_body(resp.body)
29
29
  end
30
30
 
31
31
  def delete(path)
32
- if @cert.is_certificate_expired()
33
- @cert.setup()
34
- end
32
+ @cert.setup if @cert.is_certificate_expired
35
33
  resp = @request.execute(:delete, build_url(path))
36
34
  parse_json_body(resp.body)
37
35
  end
38
36
 
39
- def post(path, body, optional_headers = {}, alternative_base_url = nil, basic_auth = false)
40
- if @cert.is_certificate_expired()
41
- @cert.setup()
42
- end
43
- resp = @request.execute(:post, build_url(path, alternative_base_url), body, optional_headers, basic_auth)
44
- return parse_json_body(resp.body) unless resp.body.empty?
37
+ def post(path, body, basic_auth = false, error_map = Evervault::Errors::LegacyErrorMap)
38
+ @cert.setup if @cert.is_certificate_expired
39
+ resp = @request.execute(:post, build_url(path), body, basic_auth, error_map)
40
+ parse_json_body(resp.body) unless resp.body.empty?
45
41
  end
46
42
 
47
- private def parse_json_body(body)
43
+ private
44
+
45
+ def parse_json_body(body)
48
46
  JSON.parse(body)
49
47
  end
50
48
 
51
- private def build_url(path, alternative_base_url = nil)
52
- return "#{@base_url}#{path}" unless alternative_base_url
53
- "#{alternative_base_url}#{path}"
49
+ def build_url(path)
50
+ "#{config.base_url}#{path}"
54
51
  end
55
52
  end
56
53
  end
@@ -1,10 +1,12 @@
1
- require "faraday"
2
- require "json"
3
- require "tempfile"
4
- require "openssl"
1
+ # frozen_string_literal: true
2
+
3
+ require 'faraday'
4
+ require 'json'
5
+ require 'tempfile'
6
+ require 'openssl'
5
7
  require 'net/http'
6
- require_relative "../version"
7
- require_relative "../errors/errors"
8
+ require_relative '../version'
9
+ require_relative '../errors/errors'
8
10
 
9
11
  module NetHTTPOverride
10
12
  @@api_key = nil
@@ -18,7 +20,7 @@ module NetHTTPOverride
18
20
  end
19
21
 
20
22
  def self.set_relay_url(value)
21
- relay_address_and_port = value.gsub(/(^\w+:|^)\/\//, '').split(":")
23
+ relay_address_and_port = value.gsub(%r{(^\w+:|^)//}, '').split(':')
22
24
  @@relay_url = relay_address_and_port[0]
23
25
  @@relay_port = relay_address_and_port[1]
24
26
  end
@@ -35,14 +37,14 @@ module NetHTTPOverride
35
37
  if @@get_decryption_domains_func.nil?
36
38
  false
37
39
  else
38
- decryption_domains = @@get_decryption_domains_func.call()
39
- decryption_domains.any? { |decryption_domain|
40
- if decryption_domain.start_with?("*")
41
- domain.end_with?(decryption_domain[1..-1])
40
+ decryption_domains = @@get_decryption_domains_func.call
41
+ decryption_domains.any? do |decryption_domain|
42
+ if decryption_domain.start_with?('*')
43
+ domain.end_with?(decryption_domain[1..])
42
44
  else
43
45
  domain == decryption_domain
44
46
  end
45
- }
47
+ end
46
48
  end
47
49
  end
48
50
 
@@ -59,9 +61,7 @@ module NetHTTPOverride
59
61
 
60
62
  def request_with_intercept(req, body = nil, &block)
61
63
  should_decrypt = NetHTTPOverride.should_decrypt(@address)
62
- if should_decrypt
63
- req["Proxy-Authorization"] = @@api_key
64
- end
64
+ req['Proxy-Authorization'] = @@api_key if should_decrypt
65
65
  request_without_intercept(req, body, &block)
66
66
  end
67
67
  end
@@ -77,58 +77,58 @@ end
77
77
  module Evervault
78
78
  module Http
79
79
  class RequestIntercept
80
- def initialize(request:, ca_host:, api_key:, base_url:, relay_url:)
81
- NetHTTPOverride.set_api_key(api_key)
82
- NetHTTPOverride.set_relay_url(relay_url)
83
-
80
+ attr_reader :config
81
+
82
+ def initialize(request:, config:)
83
+ @config = config
84
+ NetHTTPOverride.set_api_key(config.api_key)
85
+ NetHTTPOverride.set_relay_url(config.relay_url)
86
+
84
87
  @request = request
85
- @base_url = base_url
86
- @ca_host = ca_host
87
88
  @expire_date = nil
88
89
  @initial_date = nil
89
90
  end
90
91
 
91
- def is_certificate_expired()
92
+ def is_certificate_expired
92
93
  if @expire_date
93
94
  now = Time.now
94
- if now > @expire_date || now < @initial_date
95
- return true
96
- end
95
+ return true if now > @expire_date || now < @initial_date
97
96
  end
98
- return false
97
+ false
99
98
  end
100
99
 
101
100
  def setup_decryption_domains(decryption_domains)
102
- NetHTTPOverride.add_get_decryption_domains_func(-> {
101
+ NetHTTPOverride.add_get_decryption_domains_func(lambda {
103
102
  decryption_domains
104
103
  })
105
104
  end
106
105
 
107
106
  def setup_outbound_relay_config
108
- @relay_outbound_config = Evervault::Http::RelayOutboundConfig.new(base_url: @base_url, request: @request)
109
- NetHTTPOverride.add_get_decryption_domains_func(-> {
107
+ @relay_outbound_config = Evervault::Http::RelayOutboundConfig.new(base_url: config.base_url, request: @request)
108
+ NetHTTPOverride.add_get_decryption_domains_func(lambda {
110
109
  @relay_outbound_config.get_destination_domains
111
110
  })
112
111
  end
113
112
 
114
113
  def setup
115
- get_cert()
114
+ get_cert
116
115
  end
117
116
 
118
- def get_cert()
117
+ def get_cert
119
118
  ca_content = nil
120
119
  i = 0
121
120
 
122
121
  while !ca_content && i < 1
123
122
  i += 1
124
123
  begin
125
- ca_content = @request.execute("get", @ca_host, nil, {}).body
126
- rescue;
124
+ ca_content = @request.execute('get', config.ca_host).body
125
+ rescue StandardError
127
126
  end
128
127
  end
129
128
 
130
- if !ca_content || ca_content == ""
131
- raise Evervault::Errors::CertDownloadError.new("Unable to install the Evervault root certificate from #{@ca_host}")
129
+ if !ca_content || ca_content == ''
130
+ raise Evervault::Errors::EvervaultError,
131
+ "Unable to install the Evervault root certificate from #{config.ca_host}"
132
132
  end
133
133
 
134
134
  cert = OpenSSL::X509::Certificate.new ca_content
@@ -137,14 +137,11 @@ module Evervault
137
137
  end
138
138
 
139
139
  def set_cert_expire_date(cert)
140
- begin
141
- @expire_date = cert.not_after
142
- @initial_date = cert.not_before
143
- rescue => exception
144
- @expire_date = nil
145
- end
140
+ @expire_date = cert.not_after
141
+ @initial_date = cert.not_before
142
+ rescue StandardError
143
+ @expire_date = nil
146
144
  end
147
145
  end
148
146
  end
149
147
  end
150
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Evervault
2
4
  module Threading
3
5
  class RepeatedTimer
@@ -9,15 +11,15 @@ module Evervault
9
11
  end
10
12
 
11
13
  def start
12
- if !running?
13
- @thread = Thread.new do
14
- loop do
15
- sleep @interval
16
- begin
17
- @func.call
18
- rescue => e
19
- # Silently ignore exceptions
20
- end
14
+ return if running?
15
+
16
+ @thread = Thread.new do
17
+ loop do
18
+ sleep @interval
19
+ begin
20
+ @func.call
21
+ rescue StandardError
22
+ # Silently ignore exceptions
21
23
  end
22
24
  end
23
25
  end
@@ -37,4 +39,4 @@ module Evervault
37
39
  end
38
40
  end
39
41
  end
40
- end
42
+ end
@@ -1,31 +1,30 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'digest'
2
4
 
3
5
  module Evervault
4
6
  module Utils
5
7
  class ValidationUtils
6
-
7
8
  def self.validate_app_uuid_and_api_key(app_uuid, api_key)
8
9
  if app_uuid.nil?
9
- raise Evervault::Errors::AuthenticationError.new(
10
- "No App ID provided. The App ID can be retrieved in the Evervault dashboard (App Settings)."
11
- )
10
+ raise Evervault::Errors::EvervaultError,
11
+ 'No App ID provided. The App ID can be retrieved in the Evervault dashboard (App Settings).'
12
12
  end
13
13
  if api_key.nil?
14
- raise Evervault::Errors::AuthenticationError.new(
15
- "The provided App ID is invalid. The App ID can be retrieved in the Evervault dashboard (App Settings)."
16
- )
17
- end
18
- if api_key.start_with?('ev:key')
19
- # Scoped API key
20
- app_uuid_hash = Digest::SHA512.base64digest(app_uuid)[0, 6]
21
- app_uuid_hash_from_api_key = api_key.split(':')[4]
22
- if app_uuid_hash != app_uuid_hash_from_api_key
23
- raise Evervault::Errors::AuthenticationError.new(
24
- "The API key is not valid for app #{app_uuid}. Make sure to use an API key belonging to the app #{app_uuid}."
25
- )
26
- end
14
+ raise Evervault::Errors::EvervaultError,
15
+ 'The provided App ID is invalid. The App ID can be retrieved in the Evervault dashboard (App Settings).'
27
16
  end
17
+ return unless api_key.start_with?('ev:key')
18
+
19
+ # Scoped API key
20
+ app_uuid_hash = Digest::SHA512.base64digest(app_uuid)[0, 6]
21
+ app_uuid_hash_from_api_key = api_key.split(':')[4]
22
+ return unless app_uuid_hash != app_uuid_hash_from_api_key
23
+
24
+ raise Evervault::Errors::EvervaultError,
25
+ "The API key is not valid for app #{app_uuid}. Make sure to use an API key belonging to the ap'\
26
+ 'p #{app_uuid}."
28
27
  end
29
28
  end
30
29
  end
31
- end
30
+ end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Evervault
2
- VERSION = "2.0.0"
3
- EV_VERSION = {"prime256v1" => "NOC", "secp256k1" => "DUB"}
4
+ VERSION = "3.0.1"
5
+ EV_VERSION = { 'prime256v1' => 'QkTC', 'secp256k1' => 'S0lS' }.freeze
4
6
  end
data/lib/evervault.rb CHANGED
@@ -1,36 +1,52 @@
1
- require_relative "evervault/version"
2
- require_relative "evervault/client"
3
- require_relative "evervault/errors/errors"
4
- require_relative "evervault/utils/validation_utils"
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require_relative 'evervault/version'
5
+ require_relative 'evervault/client'
6
+ require_relative 'evervault/config'
7
+ require_relative 'evervault/errors/errors'
8
+ require_relative 'evervault/utils/validation_utils'
5
9
 
6
10
  module Evervault
7
11
  class << self
8
- attr_accessor :app_id
9
- attr_accessor :api_key
12
+ attr_accessor :app_id, :api_key
13
+
14
+ def encrypt(...)
15
+ client.encrypt(...)
16
+ end
10
17
 
11
- def encrypt(data)
12
- client.encrypt(data)
18
+ def decrypt(...)
19
+ client.decrypt(...)
13
20
  end
14
21
 
15
- def decrypt(data)
16
- client.decrypt(data)
22
+ def run(...)
23
+ client.run(...)
17
24
  end
18
25
 
19
- def run(function_name, encrypted_data, options = {})
20
- client.run(function_name, encrypted_data, options)
26
+ def enable_outbound_relay(...)
27
+ client.enable_outbound_relay(...)
21
28
  end
22
29
 
23
- def enable_outbound_relay(decryption_domains = nil)
24
- client.enable_outbound_relay(decryption_domains)
30
+ def create_run_token(...)
31
+ client.create_run_token(...)
25
32
  end
26
33
 
27
- def create_run_token(function_name, data = {})
28
- client.create_run_token(function_name, data)
34
+ def create_client_side_decrypt_token(data, expiry = nil)
35
+ expiry = (expiry.to_f * 1000).to_i unless expiry.nil?
36
+ client.create_token('api:decrypt', data, expiry)
29
37
  end
30
38
 
31
- private def client
32
- Evervault::Utils::ValidationUtils.validate_app_uuid_and_api_key(app_id, api_key)
33
- @client ||= Evervault::Client.new(app_uuid: app_id, api_key: api_key)
39
+ def configure(...)
40
+ client.configure(...)
41
+ end
42
+
43
+ private
44
+
45
+ def client
46
+ @client ||= begin
47
+ Evervault::Utils::ValidationUtils.validate_app_uuid_and_api_key(app_id, api_key)
48
+ Evervault::Client.new(app_uuid: app_id, api_key: api_key)
49
+ end
34
50
  end
35
51
  end
36
52
  end