evervault 2.0.0 → 3.0.1

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