Skip to content

Commit 14d7d16

Browse files
Merge pull request #10 from evervault/deirdre/pro-800-add-outbound-to-ruby-sdk
Adding outbound interception
2 parents 5f68c55 + 20b8cb9 commit 14d7d16

11 files changed

Lines changed: 748 additions & 89 deletions

File tree

lib/evervault/client.rb

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
require_relative "http/request"
2+
require_relative "http/request_handler"
3+
require_relative "http/request_intercept"
24
require_relative "crypto/client"
35
require_relative "models/cage_list"
46

@@ -9,30 +11,30 @@ class Client
911
def initialize(
1012
api_key:,
1113
base_url: "https://api.evervault.com/",
12-
cage_run_url: "https://cage.run/",
14+
cage_run_url: "https://run.evervault.com/",
15+
relay_url: "https://relay.evervault.com:8443",
16+
ca_host: "https://ca.evervault.com",
1317
request_timeout: 30,
14-
curve: 'secp256k1'
18+
curve: 'prime256v1'
1519
)
16-
@api_key = api_key
17-
@base_url = base_url
18-
@cage_run_url = cage_run_url
19-
@curve = curve
20-
@request =
21-
Evervault::Http::Request.new(
22-
api_key: api_key,
23-
timeout: request_timeout,
24-
base_url: base_url,
25-
cage_run_url: cage_run_url
20+
@request = Evervault::Http::Request.new(timeout: request_timeout, api_key: api_key)
21+
@intercept = Evervault::Http::RequestIntercept.new(
22+
request: @request, ca_host: ca_host, api_key: api_key, relay_url: relay_url
23+
)
24+
@request_handler =
25+
Evervault::Http::RequestHandler.new(
26+
request: @request, base_url: base_url, cage_run_url: cage_run_url, cert: @intercept
2627
)
27-
@crypto_client = Evervault::Crypto::Client.new(request: @request, curve: @curve)
28+
@crypto_client = Evervault::Crypto::Client.new(request_handler: @request_handler, curve: curve)
29+
@intercept.setup()
2830
end
2931

3032
def encrypt(data)
3133
@crypto_client.encrypt(data)
3234
end
3335

3436
def run(cage_name, encrypted_data, options = {})
35-
@request.post(cage_name, encrypted_data, options: options, cage_run: true)
37+
@request_handler.post(cage_name, encrypted_data, options: options, cage_run: true)
3638
end
3739

3840
def encrypt_and_run(cage_name, data, options = {})
@@ -45,8 +47,12 @@ def cages
4547
end
4648

4749
def cage_list
48-
cages = @request.get("cages")
50+
cages = @request_handler.get("cages")
4951
@cage_list ||= Evervault::Models::CageList.new(cages: cages["cages"], request: @request)
5052
end
53+
54+
def relay(decryption_domains=[])
55+
@intercept.setup_domains(decryption_domains)
56+
end
5157
end
5258
end

lib/evervault/crypto/client.rb

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,14 @@
99
module Evervault
1010
module Crypto
1111
class Client
12-
attr_reader :request
13-
def initialize(request:, curve:)
12+
attr_reader :request_handler
13+
def initialize(request_handler:, curve:)
1414
@curve = curve
1515
@p256 = Evervault::Crypto::Curves::P256.new()
1616
@ev_version = base_64_remove_padding(
1717
Base64.strict_encode64(EV_VERSION[curve])
1818
)
19-
response = request.get("cages/key")
19+
response = request_handler.get("cages/key")
2020
key = @curve == 'secp256k1' ? 'ecdhKey' : 'ecdhP256Key'
2121
@team_key = response[key]
2222
end
@@ -25,6 +25,10 @@ def encrypt(data)
2525
raise Evervault::Errors::UndefinedDataError.new(
2626
"Data is required for encryption"
2727
) if data.nil? || (data.instance_of?(String) && data.empty?)
28+
29+
raise Evervault::Errors::UnsupportedEncryptType.new(
30+
"Encryption is not supported for #{data.class}"
31+
) if !(encryptable_data?(data) || data.instance_of?(Hash) || data.instance_of?(Array))
2832

2933
traverse_and_encrypt(data)
3034
end
@@ -59,6 +63,10 @@ def encrypt(data)
5963
elsif data.instance_of?(Array)
6064
encrypted_data = data.map { |value| traverse_and_encrypt(value) }
6165
return encrypted_data
66+
else
67+
raise Evervault::Errors::UnsupportedEncryptType.new(
68+
"Encryption is not supported for #{data.class}"
69+
)
6270
end
6371
data
6472
end

lib/evervault/errors/error_map.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module Evervault
44
module Errors
55
class ErrorMap
6-
def self.raise_errors_on_failure(status_code, body)
6+
def self.raise_errors_on_failure(status_code, body, headers)
77
return if status_code < 400
88
case status_code
99
when 404
@@ -13,7 +13,11 @@ def self.raise_errors_on_failure(status_code, body)
1313
when 401
1414
raise AuthenticationError.new("Unauthorized")
1515
when 403
16-
raise AuthenticationError.new("Forbidden")
16+
if (headers.include? "x-evervault-error-code") && (headers["x-evervault-error-code"] == "forbidden-ip-error")
17+
raise ForbiddenIPError.new("IP is not present in Cage whitelist")
18+
else
19+
raise AuthenticationError.new("Forbidden")
20+
end
1721
when 500
1822
raise ServerError.new("Server Error")
1923
when 502

lib/evervault/errors/errors.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,5 +23,11 @@ class UndefinedDataError < EvervaultError; end
2323
class InvalidPublicKeyError < EvervaultError; end
2424

2525
class UnexpectedError < EvervaultError; end
26+
27+
class CertDownloadError < EvervaultError; end
28+
29+
class UnsupportedEncryptType < EvervaultError; end
30+
31+
class ForbiddenIPError < EvervaultError; end
2632
end
2733
end

lib/evervault/http/request.rb

Lines changed: 12 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -6,41 +6,24 @@
66
module Evervault
77
module Http
88
class Request
9-
def initialize(api_key:, base_url:, cage_run_url:, timeout:)
10-
@api_key = api_key
9+
def initialize(timeout:, api_key:)
1110
@timeout = timeout
12-
@base_url = base_url
13-
@cage_run_url = cage_run_url
14-
end
15-
16-
def get(path, params = nil)
17-
execute(:get, build_url(path), params)
18-
end
19-
20-
def put(path, params)
21-
execute(:put, build_url(path), params)
22-
end
23-
24-
def delete(path, params)
25-
execute(:delete, build_url(path), params)
26-
end
27-
28-
def post(path, params, options: {}, cage_run: false)
29-
execute(:post, build_url(path, cage_run), params, build_cage_run_headers(options, cage_run))
30-
end
31-
32-
private def build_url(path, cage_run = false)
33-
return "#{@base_url}#{path}" unless cage_run
34-
"#{@cage_run_url}#{path}"
11+
@api_key = api_key
3512
end
3613

37-
def execute(method, url, params, optional_headers = {})
14+
def execute(method, url, params, optional_headers = {}, is_ca = false)
3815
resp = Faraday.send(method, url) do |req|
3916
req.body = params.nil? || params.empty? ? nil : params.to_json
4017
req.headers = build_headers(optional_headers)
18+
req.options.timeout = @timeout
4119
end
42-
return JSON.parse(resp.body) if resp.status >= 200 && resp.status <= 300
43-
Evervault::Errors::ErrorMap.raise_errors_on_failure(resp.status, resp.body)
20+
if resp.status >= 200 && resp.status <= 300
21+
if is_ca
22+
return resp.body
23+
end
24+
return JSON.parse(resp.body)
25+
end
26+
Evervault::Errors::ErrorMap.raise_errors_on_failure(resp.status, resp.body, resp.headers)
4427
end
4528

4629
private def build_headers(optional_headers)
@@ -49,27 +32,9 @@ def execute(method, url, params, optional_headers = {})
4932
"Accept": "application/json",
5033
"Content-Type": "application/json",
5134
"User-Agent": "evervault-ruby/#{VERSION}",
52-
"Api-Key": @api_key
35+
"Api-Key": @api_key,
5336
})
5437
end
55-
56-
private def build_cage_run_headers(options, cage_run = false)
57-
optional_headers = {}
58-
return optional_headers unless cage_run
59-
if options.key?(:async)
60-
if options[:async]
61-
optional_headers["x-async"] = "true"
62-
end
63-
options.delete(:async)
64-
end
65-
if options.key?(:version)
66-
if options[:version].is_a? Integer
67-
optional_headers["x-version-id"] = options[:version].to_s
68-
end
69-
options.delete(:version)
70-
end
71-
optional_headers.merge(options)
72-
end
7338
end
7439
end
7540
end
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
require "faraday"
2+
require "json"
3+
require_relative "../version"
4+
require_relative "../errors/error_map"
5+
6+
module Evervault
7+
module Http
8+
class RequestHandler
9+
def initialize(request:, base_url:, cage_run_url:, cert:)
10+
@request = request
11+
@base_url = base_url
12+
@cage_run_url = cage_run_url
13+
@cert = cert
14+
end
15+
16+
def get(path, params = nil)
17+
if @cert.is_certificate_expired()
18+
@cert.setup()
19+
end
20+
@request.execute(:get, build_url(path), params)
21+
end
22+
23+
def put(path, params)
24+
if @cert.is_certificate_expired()
25+
@cert.setup()
26+
end
27+
@request.execute(:put, build_url(path), params)
28+
end
29+
30+
def delete(path, params)
31+
if @cert.is_certificate_expired()
32+
@cert.setup()
33+
end
34+
@request.execute(:delete, build_url(path), params)
35+
end
36+
37+
def post(path, params, options: {}, cage_run: false)
38+
if @cert.is_certificate_expired()
39+
@cert.setup()
40+
end
41+
@request.execute(:post, build_url(path, cage_run), params, build_cage_run_headers(options, cage_run))
42+
end
43+
44+
private def build_url(path, cage_run = false)
45+
return "#{@base_url}#{path}" unless cage_run
46+
"#{@cage_run_url}#{path}"
47+
end
48+
49+
private def build_cage_run_headers(options, cage_run = false)
50+
optional_headers = {}
51+
return optional_headers unless cage_run
52+
if options.key?(:async)
53+
if options[:async]
54+
optional_headers["x-async"] = "true"
55+
end
56+
options.delete(:async)
57+
end
58+
if options.key?(:version)
59+
if options[:version].is_a? Integer
60+
optional_headers["x-version-id"] = options[:version].to_s
61+
end
62+
options.delete(:version)
63+
end
64+
optional_headers.merge(options)
65+
end
66+
end
67+
end
68+
end

0 commit comments

Comments
 (0)