konfidant 1.3.0 → 1.4.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/LICENSE +21 -0
- data/README.md +8 -1
- data/lib/konfidant/client.rb +31 -5
- data/lib/konfidant/knf.rb +8 -8
- data/lib/konfidant/types.rb +11 -0
- data/lib/konfidant/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2dd0f63c7378b463ca026143137aff868ad08b6e7d8b8a8672120fa283138326
|
|
4
|
+
data.tar.gz: 9b77783dc745eb9919f0bb864cd506899d2651b957af20da128639329797c5a2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f704dc0b64fdb6820a0a9220f4bbb8abc876d402ee96bffdbac2b4187e53953528ae057661a57da014879ed2dfda51dda32dbc1684a25601b370ec8693d87037
|
|
7
|
+
data.tar.gz: 5f266d4494ae32900e035b0b75d23150441c9630f006fb6d22263ad7e4edf61736d59e793ea333b82953ffa217b74954dba2dc0ab4545c73aff52be249415571
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Konfidant
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
[](https://github.com/konfidant/sdk-ruby/actions/workflows/test.yml)
|
|
4
4
|
[](https://app.codacy.com/gh/konfidant/sdk-ruby/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
|
5
5
|
[](https://app.codacy.com/gh/konfidant/sdk-ruby/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)
|
|
6
|
+
[](https://sonarcloud.io/summary/new_code?id=konfidant_sdk-ruby)
|
|
6
7
|
|
|
7
|
-
Official Ruby SDK for the [Konfidant](https://www.konfidant.app) API.
|
|
8
|
+
Official Ruby SDK for the [Konfidant](https://www.konfidant.app?utm_source=github&utm_medium=rubysdk&utm_campaign=github) API.
|
|
8
9
|
|
|
9
10
|
Konfidant lets you share secrets — text and files — that self-destruct after being read.
|
|
10
11
|
|
|
@@ -150,6 +151,12 @@ Returns `shares` (`Array<Konfidant::Share>` with `type`, `file_size_bytes`, `cre
|
|
|
150
151
|
`accessed_at`, `created_by`) and `pagination` (`total`, `limit`, `offset`, `has_more`). File names are not
|
|
151
152
|
available: the server never sees them.
|
|
152
153
|
|
|
154
|
+
### `client.me` → `Konfidant::KeyInfo`
|
|
155
|
+
|
|
156
|
+
Describes the API key without creating a share: `organization` (`id`, `name`), `api_key` (`name`, `environment`,
|
|
157
|
+
`scopes`, `rate_limit_per_minute`, `expires_at`) and the plan's `limits` (`max_ttl_hours`, `max_file_size_mb`). It
|
|
158
|
+
needs no scope and does not count as an API share. Use it to check a key before storing it.
|
|
159
|
+
|
|
153
160
|
### `Konfidant::Knf`
|
|
154
161
|
|
|
155
162
|
The KNF1 implementation is public for advanced use and interoperability:
|
data/lib/konfidant/client.rb
CHANGED
|
@@ -9,6 +9,11 @@ module Konfidant
|
|
|
9
9
|
# Konfidant API client. All content is encrypted locally (KNF1) before it leaves this process; the key is only
|
|
10
10
|
# ever placed in the returned share link's URL fragment and is never sent to Konfidant.
|
|
11
11
|
class Client
|
|
12
|
+
CONTENT_TYPE = 'Content-Type'
|
|
13
|
+
JSON_MIME = 'application/json'
|
|
14
|
+
BINARY_MIME = 'application/octet-stream'
|
|
15
|
+
private_constant :CONTENT_TYPE, :JSON_MIME, :BINARY_MIME
|
|
16
|
+
|
|
12
17
|
def initialize(api_key:, base_url: nil, http_timeout: DEFAULT_TIMEOUT)
|
|
13
18
|
raise ArgumentError, 'api_key is required' if api_key.nil? || api_key.empty?
|
|
14
19
|
|
|
@@ -78,7 +83,7 @@ module Konfidant
|
|
|
78
83
|
req = Net::HTTP::Put.new(uri)
|
|
79
84
|
upload.upload_headers.each { |name, value| req[name] = value.to_s }
|
|
80
85
|
# Only when the server did not specify one: Net::HTTP would otherwise send application/x-www-form-urlencoded.
|
|
81
|
-
req[
|
|
86
|
+
req[CONTENT_TYPE] ||= BINARY_MIME
|
|
82
87
|
req['Content-Length'] = size.to_s
|
|
83
88
|
if ciphertext.is_a?(String)
|
|
84
89
|
req.body = ciphertext.b
|
|
@@ -117,6 +122,27 @@ module Konfidant
|
|
|
117
122
|
)
|
|
118
123
|
end
|
|
119
124
|
|
|
125
|
+
# Describes the API key: its organization, scopes and rate limit, and the plan's share limits. Use it to validate
|
|
126
|
+
# a key without creating a share. Needs no scope and does not count as an API share.
|
|
127
|
+
def me
|
|
128
|
+
body = api_request(:get, '/api/v1/me')
|
|
129
|
+
key = body['api_key']
|
|
130
|
+
KeyInfo.new(
|
|
131
|
+
organization: Organization.new(id: body['organization']['id'], name: body['organization']['name']),
|
|
132
|
+
api_key: ApiKeyInfo.new(
|
|
133
|
+
name: key['name'],
|
|
134
|
+
environment: key['environment'],
|
|
135
|
+
scopes: key['scopes'],
|
|
136
|
+
rate_limit_per_minute: key['rate_limit_per_minute'],
|
|
137
|
+
expires_at: key['expires_at']
|
|
138
|
+
),
|
|
139
|
+
limits: PlanLimits.new(
|
|
140
|
+
max_ttl_hours: body['limits']['max_ttl_hours'],
|
|
141
|
+
max_file_size_mb: body['limits']['max_file_size_mb']
|
|
142
|
+
)
|
|
143
|
+
)
|
|
144
|
+
end
|
|
145
|
+
|
|
120
146
|
# Recipient side: fetches the ciphertext behind +share_url+ (consuming its single-use token) and decrypts it
|
|
121
147
|
# locally. Only the token is sent; the key and the API key are not. Raises ApiError (410) if the share was
|
|
122
148
|
# already opened or has expired, KnfError if decryption fails.
|
|
@@ -124,8 +150,8 @@ module Konfidant
|
|
|
124
150
|
origin, token, key = Knf.parse_share_url(share_url)
|
|
125
151
|
uri = URI.parse("#{origin}/api/download")
|
|
126
152
|
req = Net::HTTP::Post.new(uri)
|
|
127
|
-
req[
|
|
128
|
-
req['Accept']
|
|
153
|
+
req[CONTENT_TYPE] = JSON_MIME
|
|
154
|
+
req['Accept'] = BINARY_MIME
|
|
129
155
|
req.body = JSON.generate({ t: token })
|
|
130
156
|
|
|
131
157
|
ciphertext = handle_response(perform(uri, req))
|
|
@@ -177,7 +203,7 @@ module Konfidant
|
|
|
177
203
|
req = method == :get ? Net::HTTP::Get.new(uri) : Net::HTTP::Post.new(uri)
|
|
178
204
|
req['Authorization'] = "Bearer #{@api_key}"
|
|
179
205
|
if payload
|
|
180
|
-
req[
|
|
206
|
+
req[CONTENT_TYPE] = JSON_MIME
|
|
181
207
|
req.body = JSON.generate(payload)
|
|
182
208
|
elsif method == :post
|
|
183
209
|
req.body = ''
|
|
@@ -188,7 +214,7 @@ module Konfidant
|
|
|
188
214
|
|
|
189
215
|
def handle_response(resp)
|
|
190
216
|
content_type = resp['content-type'] || ''
|
|
191
|
-
body = content_type.include?(
|
|
217
|
+
body = content_type.include?(JSON_MIME) ? parse_json(resp.body) : resp.body
|
|
192
218
|
|
|
193
219
|
return body if success?(resp)
|
|
194
220
|
|
data/lib/konfidant/knf.rb
CHANGED
|
@@ -36,6 +36,7 @@ module Konfidant
|
|
|
36
36
|
META_LENGTH_PREFIX = 4
|
|
37
37
|
CIPHER = 'aes-256-gcm'.freeze
|
|
38
38
|
KEY_PATTERN = /\A[A-Za-z0-9_-]{43}\z/
|
|
39
|
+
METADATA_TRUNCATED = 'Metadata truncated'.freeze
|
|
39
40
|
|
|
40
41
|
# Result of a successful decryption. +data+ is a binary String; +kind+ is "text" or "file".
|
|
41
42
|
Decrypted = Data.define(:kind, :name, :mime, :data) do
|
|
@@ -47,7 +48,7 @@ module Konfidant
|
|
|
47
48
|
end
|
|
48
49
|
end
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
extend self
|
|
51
52
|
|
|
52
53
|
# 32 random bytes from the OS CSPRNG. Use a fresh key for every share.
|
|
53
54
|
def generate_key
|
|
@@ -165,6 +166,8 @@ module Konfidant
|
|
|
165
166
|
raise ArgumentError, 'Invalid share URL'
|
|
166
167
|
end
|
|
167
168
|
|
|
169
|
+
private
|
|
170
|
+
|
|
168
171
|
# --- internal helpers --------------------------------------------------------------------------------------
|
|
169
172
|
|
|
170
173
|
def check_key!(key)
|
|
@@ -218,10 +221,10 @@ module Konfidant
|
|
|
218
221
|
end
|
|
219
222
|
|
|
220
223
|
def decode_stream(stream)
|
|
221
|
-
raise KnfError,
|
|
224
|
+
raise KnfError, METADATA_TRUNCATED if stream.bytesize < META_LENGTH_PREFIX
|
|
222
225
|
|
|
223
226
|
meta_length = stream.byteslice(0, META_LENGTH_PREFIX).unpack1('N')
|
|
224
|
-
raise KnfError,
|
|
227
|
+
raise KnfError, METADATA_TRUNCATED if META_LENGTH_PREFIX + meta_length > stream.bytesize
|
|
225
228
|
|
|
226
229
|
meta = stream.byteslice(META_LENGTH_PREFIX, meta_length)
|
|
227
230
|
kind, name, mime = decode_metadata(meta)
|
|
@@ -230,13 +233,13 @@ module Konfidant
|
|
|
230
233
|
end
|
|
231
234
|
|
|
232
235
|
def decode_metadata(meta)
|
|
233
|
-
raise KnfError,
|
|
236
|
+
raise KnfError, METADATA_TRUNCATED if meta.bytesize < META_FIXED_SIZE
|
|
234
237
|
|
|
235
238
|
kind = KINDS.key(meta.getbyte(0))
|
|
236
239
|
raise KnfError, 'Unknown content kind' unless kind
|
|
237
240
|
|
|
238
241
|
name_length = meta.byteslice(1, 2).unpack1('n')
|
|
239
|
-
raise KnfError,
|
|
242
|
+
raise KnfError, METADATA_TRUNCATED if 3 + name_length + 2 > meta.bytesize
|
|
240
243
|
|
|
241
244
|
mime_length = meta.byteslice(3 + name_length, 2).unpack1('n')
|
|
242
245
|
raise KnfError, 'Metadata length mismatch' unless META_FIXED_SIZE + name_length + mime_length == meta.bytesize
|
|
@@ -268,9 +271,6 @@ module Konfidant
|
|
|
268
271
|
value.nil? || value.empty?
|
|
269
272
|
end
|
|
270
273
|
|
|
271
|
-
private_class_method :check_key!, :check_chunk_size!, :build_header, :parse_header, :chunk_nonce, :seal,
|
|
272
|
-
:open_sealed, :decode_stream, :decode_metadata, :utf8_bytes, :utf8_string, :blank?
|
|
273
|
-
|
|
274
274
|
# Streaming KNF1 encryptor with an IO-like read interface. #size is the exact ciphertext length, known up front
|
|
275
275
|
# so it can be declared to the server (ciphertext_size) and sent as Content-Length.
|
|
276
276
|
class Encryptor
|
data/lib/konfidant/types.rb
CHANGED
|
@@ -24,4 +24,15 @@ module Konfidant
|
|
|
24
24
|
Pagination = Data.define(:total, :limit, :offset, :has_more)
|
|
25
25
|
|
|
26
26
|
ListSharesResponse = Data.define(:shares, :pagination)
|
|
27
|
+
|
|
28
|
+
# Result of Client#me: the key's organization, the key itself and the plan's share limits.
|
|
29
|
+
Organization = Data.define(:id, :name)
|
|
30
|
+
|
|
31
|
+
# +expires_at+ is nil if the key never expires.
|
|
32
|
+
ApiKeyInfo = Data.define(:name, :environment, :scopes, :rate_limit_per_minute, :expires_at)
|
|
33
|
+
|
|
34
|
+
# +max_file_size_mb+ is the largest file before encryption.
|
|
35
|
+
PlanLimits = Data.define(:max_ttl_hours, :max_file_size_mb)
|
|
36
|
+
|
|
37
|
+
KeyInfo = Data.define(:organization, :api_key, :limits)
|
|
27
38
|
end
|
data/lib/konfidant/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: konfidant
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Konfidant
|
|
@@ -85,6 +85,7 @@ executables: []
|
|
|
85
85
|
extensions: []
|
|
86
86
|
extra_rdoc_files: []
|
|
87
87
|
files:
|
|
88
|
+
- LICENSE
|
|
88
89
|
- README.md
|
|
89
90
|
- lib/konfidant.rb
|
|
90
91
|
- lib/konfidant/client.rb
|