Version not found. Please check the version and try again.
open-banking-io 0.1.0 → 0.2.0
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.
- checksums.yaml +4 -4
- data/README.md +2 -0
- data/lib/open_banking_io/client.rb +14 -7
- data/lib/open_banking_io/envelope.rb +13 -2
- data/lib/open_banking_io/version.rb +1 -1
- metadata +63 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 4b6ab2de9b335f7edc7203e5723066756103876c5f986dfd6ac6ff1ac7bf263f
|
|
4
|
+
data.tar.gz: 4ede77b0da39a399387d49d7b47ec4c76b7c7277c4983b61d928fbca320adf7f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e8af173269a9036665b494976ccb5292754a6984f6f18f356e2e7946edc6cd7387919cf348c73d226f96bed1ae6e65dc6fb35b8881a64dfc5788b6574c53a8b9
|
|
7
|
+
data.tar.gz: 5a91e86f5fdfaf166b1e1fe691efb86fc39cdeb2b9246838af679b2c8693f222100d6dafaa1db92df1ea976ec87b9947072eef93ee553f2e46090c0b34cf4472
|
data/README.md
CHANGED
|
@@ -49,6 +49,8 @@ client = OpenBankingIO::Client.new(
|
|
|
49
49
|
|
|
50
50
|
Amounts are exposed as `BigDecimal`. Models are immutable keyword-initialised `Struct`s.
|
|
51
51
|
|
|
52
|
+
Every request sets connect/read timeouts (15s/60s) and a `User-Agent: open-banking-io/ruby/<version>` header.
|
|
53
|
+
|
|
52
54
|
## Encryption
|
|
53
55
|
|
|
54
56
|
Envelopes use **ECDH P-256 → HKDF-SHA256 → AES-256-GCM**, implemented entirely with Ruby's OpenSSL
|
|
@@ -7,6 +7,7 @@ require "bigdecimal"
|
|
|
7
7
|
|
|
8
8
|
require_relative "envelope"
|
|
9
9
|
require_relative "models"
|
|
10
|
+
require_relative "version"
|
|
10
11
|
|
|
11
12
|
module OpenBankingIO
|
|
12
13
|
# Raised when the API returns a non-success HTTP status.
|
|
@@ -28,14 +29,15 @@ module OpenBankingIO
|
|
|
28
29
|
class Client
|
|
29
30
|
DEFAULT_OPEN_TIMEOUT = 15
|
|
30
31
|
DEFAULT_READ_TIMEOUT = 60
|
|
32
|
+
USER_AGENT = "open-banking-io/ruby/#{VERSION}".freeze
|
|
31
33
|
|
|
32
34
|
# Builds a client from a credentials-bundle JSON string or a path to a bundle file.
|
|
33
35
|
def self.from_credentials(path_or_json)
|
|
34
36
|
raw = if File.file?(path_or_json.to_s)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
File.read(path_or_json)
|
|
38
|
+
else
|
|
39
|
+
path_or_json
|
|
40
|
+
end
|
|
39
41
|
|
|
40
42
|
bundle = JSON.parse(raw)
|
|
41
43
|
api_base_url = bundle["apiBaseUrl"].to_s
|
|
@@ -108,7 +110,7 @@ module OpenBankingIO
|
|
|
108
110
|
raise ArgumentError, "Account has no active session (reconnect required) -- cannot sync"
|
|
109
111
|
end
|
|
110
112
|
|
|
111
|
-
result = post_json("api/accounts/#{account_id}/sync", {
|
|
113
|
+
result = post_json("api/accounts/#{account_id}/sync", {"uid" => uid})
|
|
112
114
|
SyncResult.new(
|
|
113
115
|
new_transactions: result["newTransactions"] || 0,
|
|
114
116
|
total_fetched: result["totalFetched"] || 0
|
|
@@ -120,10 +122,10 @@ module OpenBankingIO
|
|
|
120
122
|
items = []
|
|
121
123
|
account_wires.each do |a|
|
|
122
124
|
uid = decrypt_uid(a)
|
|
123
|
-
items << {
|
|
125
|
+
items << {"accountId" => a["id"], "uid" => uid} unless uid.nil?
|
|
124
126
|
end
|
|
125
127
|
|
|
126
|
-
result = post_json("api/sync", {
|
|
128
|
+
result = post_json("api/sync", {"items" => items})
|
|
127
129
|
SyncAllResult.new(
|
|
128
130
|
accounts: result["accounts"] || 0,
|
|
129
131
|
new_transactions: result["newTransactions"] || 0
|
|
@@ -228,6 +230,10 @@ module OpenBankingIO
|
|
|
228
230
|
uri.query = URI.encode_www_form(params)
|
|
229
231
|
end
|
|
230
232
|
|
|
233
|
+
# `path` is an internal, library-controlled API route resolved against the configured
|
|
234
|
+
# base URI (see #resolve), not user-supplied file/URL input. This is an HTTP API client;
|
|
235
|
+
# issuing the request is its purpose.
|
|
236
|
+
# nosemgrep: ruby.rails.security.audit.avoid-tainted-http-request.avoid-tainted-http-request
|
|
231
237
|
request = Net::HTTP::Get.new(uri)
|
|
232
238
|
send_request(uri, request)
|
|
233
239
|
end
|
|
@@ -247,6 +253,7 @@ module OpenBankingIO
|
|
|
247
253
|
def send_request(uri, request)
|
|
248
254
|
request["X-Api-Key"] = @api_key
|
|
249
255
|
request["Accept"] = "application/json"
|
|
256
|
+
request["User-Agent"] = USER_AGENT
|
|
250
257
|
|
|
251
258
|
http = Net::HTTP.new(uri.host, uri.port)
|
|
252
259
|
http.use_ssl = (uri.scheme == "https")
|
|
@@ -15,7 +15,7 @@ module OpenBankingIO
|
|
|
15
15
|
POINT_LEN = 65
|
|
16
16
|
NONCE_LEN = 12
|
|
17
17
|
TAG_LEN = 16
|
|
18
|
-
HKDF_SALT = ("\x00".b * 32)
|
|
18
|
+
HKDF_SALT = ("\x00".b * 32)
|
|
19
19
|
HKDF_INFO = "bank.core.ci/zk/v1".b.freeze
|
|
20
20
|
GROUP = OpenSSL::PKey::EC::Group.new("prime256v1")
|
|
21
21
|
|
|
@@ -43,7 +43,7 @@ module OpenBankingIO
|
|
|
43
43
|
tag = envelope_bytes.byteslice(1 + POINT_LEN + NONCE_LEN, TAG_LEN)
|
|
44
44
|
ciphertext = envelope_bytes.byteslice((1 + POINT_LEN + NONCE_LEN + TAG_LEN)..) || "".b
|
|
45
45
|
|
|
46
|
-
pub =
|
|
46
|
+
pub = decode_public_point(eph_pub_bytes)
|
|
47
47
|
shared = private_key.dh_compute_key(pub)
|
|
48
48
|
|
|
49
49
|
key = OpenSSL::KDF.hkdf(
|
|
@@ -63,6 +63,17 @@ module OpenBankingIO
|
|
|
63
63
|
cipher.update(ciphertext) + cipher.final
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
+
# Parses the 65-byte raw ephemeral public key into a P-256 point.
|
|
67
|
+
#
|
|
68
|
+
# A malformed or off-curve point makes +EC::Point.new+ raise an OpenSSL-internal
|
|
69
|
+
# error; we wrap it in a clean +ArgumentError+ so callers see a consistent envelope
|
|
70
|
+
# error rather than a leaking implementation detail.
|
|
71
|
+
def decode_public_point(eph_pub_bytes)
|
|
72
|
+
OpenSSL::PKey::EC::Point.new(GROUP, OpenSSL::BN.new(eph_pub_bytes, 2))
|
|
73
|
+
rescue OpenSSL::PKey::EC::Point::Error, OpenSSL::BNError => e
|
|
74
|
+
raise ArgumentError, "Invalid ephemeral public key in envelope: #{e.message}"
|
|
75
|
+
end
|
|
76
|
+
|
|
66
77
|
# Decrypts a base64 envelope and parses its JSON payload. +nil+ in -> +nil+ out.
|
|
67
78
|
def decrypt_to_json(private_key, envelope_b64)
|
|
68
79
|
return nil if envelope_b64.nil?
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: open-banking-io
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- open-banking.io
|
|
@@ -65,6 +65,68 @@ dependencies:
|
|
|
65
65
|
- - "~>"
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: '3.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: simplecov
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0.22'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0.22'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: simplecov-cobertura
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '2.1'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '2.1'
|
|
96
|
+
- !ruby/object:Gem::Dependency
|
|
97
|
+
name: rexml
|
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '3.3'
|
|
103
|
+
- - "<"
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: '3.4'
|
|
106
|
+
type: :development
|
|
107
|
+
prerelease: false
|
|
108
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
109
|
+
requirements:
|
|
110
|
+
- - "~>"
|
|
111
|
+
- !ruby/object:Gem::Version
|
|
112
|
+
version: '3.3'
|
|
113
|
+
- - "<"
|
|
114
|
+
- !ruby/object:Gem::Version
|
|
115
|
+
version: '3.4'
|
|
116
|
+
- !ruby/object:Gem::Dependency
|
|
117
|
+
name: standard
|
|
118
|
+
requirement: !ruby/object:Gem::Requirement
|
|
119
|
+
requirements:
|
|
120
|
+
- - "~>"
|
|
121
|
+
- !ruby/object:Gem::Version
|
|
122
|
+
version: '1.0'
|
|
123
|
+
type: :development
|
|
124
|
+
prerelease: false
|
|
125
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - "~>"
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: '1.0'
|
|
68
130
|
description: Authenticates with your API key and decrypts open-banking.io's zero-knowledge
|
|
69
131
|
data envelopes locally with your exported private key (ECDH P-256 -> HKDF-SHA256
|
|
70
132
|
-> AES-256-GCM). The service only ever returns ciphertext it cannot read.
|