konfidant 0.7.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 +7 -0
- data/README.md +343 -0
- data/lib/konfidant/client.rb +185 -0
- data/lib/konfidant/errors.rb +11 -0
- data/lib/konfidant/types.rb +31 -0
- data/lib/konfidant/version.rb +3 -0
- data/lib/konfidant.rb +4 -0
- metadata +115 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: d76590436720d9998f06a04fffa4a5a06b3aff597c675e6b4a1eafe770cd383f
|
|
4
|
+
data.tar.gz: 2e09ccdb885e047fdefe69faeba3289a919052534d07ef0bc5040a0e91256b14
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 6e858d0172471e5e88261696bc2201307e582f4003eb588ee6308fb0c1e2d280c72e6673b5329c00b2efc64b204a4abb9adacd848700d6a2daaeb04fe491a5c1
|
|
7
|
+
data.tar.gz: 1ecd149e3f5efed0fb906b75499789610b5305dbb816b95dbad72074a022937a1f42126556faedd248d9a96e71a5c8e40d87fbb06e4cb2f4f1736cf443b9a691
|
data/README.md
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
# konfidant-ruby
|
|
2
|
+
|
|
3
|
+
[](https://github.com/konfidant/sdk-ruby/actions/workflows/test.yml)
|
|
4
|
+
[](https://app.codacy.com/gh/konfidant/sdk-ruby/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
|
|
5
|
+
[](https://app.codacy.com/gh/konfidant/sdk-ruby/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_coverage)
|
|
6
|
+
|
|
7
|
+
Official Ruby SDK for the [Konfidant](https://www.konfidant.app) API.
|
|
8
|
+
|
|
9
|
+
Konfidant lets you share secrets — encrypted text and files — that self-destruct after being read.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
Add to your `Gemfile`:
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
gem 'konfidant'
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Then run:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
bundle install
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Or install directly:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
gem install konfidant
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
## Quick start
|
|
36
|
+
|
|
37
|
+
```ruby
|
|
38
|
+
require 'konfidant'
|
|
39
|
+
|
|
40
|
+
client = Konfidant::Client.new(api_key: 'your-api-key')
|
|
41
|
+
|
|
42
|
+
result = client.share_text(text: 'super-secret-password', ttl_hours: 24)
|
|
43
|
+
|
|
44
|
+
puts "Share this link: #{result.share_url}"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## Authentication
|
|
50
|
+
|
|
51
|
+
All requests require a Bearer API key. Generate one from the [Konfidant dashboard](https://www.konfidant.app).
|
|
52
|
+
|
|
53
|
+
```ruby
|
|
54
|
+
client = Konfidant::Client.new(api_key: ENV['KONFIDANT_API_KEY'])
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
---
|
|
58
|
+
|
|
59
|
+
## API Reference
|
|
60
|
+
|
|
61
|
+
### `Konfidant::Client.new`
|
|
62
|
+
|
|
63
|
+
| Option | Type | Required | Description |
|
|
64
|
+
|----------------|-----------|----------|----------------------------------------------------------------------|
|
|
65
|
+
| `api_key` | `String` | Yes | Your Konfidant API key |
|
|
66
|
+
| `base_url` | `String` | No | Override the base URL (default: `https://www.konfidant.app`) |
|
|
67
|
+
| `http_timeout` | `Integer` | No | Per-request HTTP timeout in seconds (default: `120`; `nil` disables) |
|
|
68
|
+
|
|
69
|
+
Raises `ArgumentError` if `api_key` is nil or empty.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
### `client.share_text(text:, ttl_hours:)`
|
|
74
|
+
|
|
75
|
+
Encrypt and share a text message.
|
|
76
|
+
|
|
77
|
+
| Argument | Type | Description |
|
|
78
|
+
|-------------|-----------|--------------------------|
|
|
79
|
+
| `text` | `String` | The secret text to share |
|
|
80
|
+
| `ttl_hours` | `Integer` | Time-to-live in hours |
|
|
81
|
+
|
|
82
|
+
Returns a `Konfidant::ShareTextResponse`:
|
|
83
|
+
|
|
84
|
+
| Field | Type | Description |
|
|
85
|
+
|----------------|-----------|---------------------------------------------|
|
|
86
|
+
| `text_id` | `String` | Unique ID of the shared text |
|
|
87
|
+
| `share_url` | `String` | One-time download link to send to recipient |
|
|
88
|
+
| `expires_at` | `String` | Expiry datetime |
|
|
89
|
+
| `verified_burn`| `Boolean` | Whether burn-on-read is verified |
|
|
90
|
+
|
|
91
|
+
#### Example: share text
|
|
92
|
+
|
|
93
|
+
```ruby
|
|
94
|
+
result = client.share_text(text: 'db-password: hunter2', ttl_hours: 48)
|
|
95
|
+
|
|
96
|
+
puts result.share_url # send to recipient
|
|
97
|
+
puts result.expires_at
|
|
98
|
+
puts result.verified_burn
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
### `client.share_file(filename:, file_size:, ttl_hours:)`
|
|
104
|
+
|
|
105
|
+
Request a presigned upload URL for a file. Use the returned response with `upload_file` to complete
|
|
106
|
+
the upload, then poll `get_file_status` for the share link.
|
|
107
|
+
|
|
108
|
+
> For a one-call convenience wrapper, see [`share_and_upload_file`](#clientshare_and_upload_file).
|
|
109
|
+
|
|
110
|
+
| Argument | Type | Description |
|
|
111
|
+
|-------------|-----------|----------------------------------|
|
|
112
|
+
| `filename` | `String` | Original filename with extension |
|
|
113
|
+
| `file_size` | `Integer` | File size in bytes |
|
|
114
|
+
| `ttl_hours` | `Integer` | Time-to-live in hours |
|
|
115
|
+
|
|
116
|
+
Returns a `Konfidant::ShareFileResponse`:
|
|
117
|
+
|
|
118
|
+
| Field | Type | Description |
|
|
119
|
+
|--------------------|-------------------------------|-------------------------------------------------|
|
|
120
|
+
| `upload_url` | `String` | Short-lived presigned S3 PUT URL |
|
|
121
|
+
| `file_key` | `String` | Use with `get_file_status` and `upload_file` |
|
|
122
|
+
| `poll_url` | `String` | Convenience URL for status polling |
|
|
123
|
+
| `metadata_headers` | `Konfidant::FileMetadataHeaders` | Required S3 headers — passed by `upload_file` |
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### `client.upload_file(io:, size:, content_type:, presigned:)`
|
|
128
|
+
|
|
129
|
+
Upload file bytes to the presigned S3 URL from `share_file`. Automatically attaches the required
|
|
130
|
+
S3 metadata headers. Does **not** send the Konfidant `Authorization` header to S3.
|
|
131
|
+
|
|
132
|
+
| Argument | Type | Description |
|
|
133
|
+
|----------------|-----------------------------|---------------------------------------|
|
|
134
|
+
| `io` | `IO` | Readable IO object (File, StringIO) |
|
|
135
|
+
| `size` | `Integer` | Content-Length in bytes |
|
|
136
|
+
| `content_type` | `String` | MIME type (e.g. `application/pdf`) |
|
|
137
|
+
| `presigned` | `Konfidant::ShareFileResponse` | Full response from `share_file` |
|
|
138
|
+
|
|
139
|
+
Returns `nil` on success. Raises `Konfidant::ApiError` on S3 error.
|
|
140
|
+
|
|
141
|
+
#### Example: manual three-step flow
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
file = File.open('report.pdf', 'rb')
|
|
145
|
+
size = File.size('report.pdf')
|
|
146
|
+
|
|
147
|
+
# Step 1 – get presigned URL
|
|
148
|
+
presigned = client.share_file(
|
|
149
|
+
filename: 'report.pdf',
|
|
150
|
+
file_size: size,
|
|
151
|
+
ttl_hours: 72
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
# Step 2 – upload to S3
|
|
155
|
+
client.upload_file(
|
|
156
|
+
io: file,
|
|
157
|
+
size: size,
|
|
158
|
+
content_type: 'application/pdf',
|
|
159
|
+
presigned: presigned
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
# Step 3 – poll for share link
|
|
163
|
+
loop do
|
|
164
|
+
status = client.get_file_status(presigned.file_key)
|
|
165
|
+
if status.status == 'complete'
|
|
166
|
+
puts "Share URL: #{status.share_url}"
|
|
167
|
+
break
|
|
168
|
+
end
|
|
169
|
+
sleep 2
|
|
170
|
+
end
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
---
|
|
174
|
+
|
|
175
|
+
### `client.get_file_status(file_key)`
|
|
176
|
+
|
|
177
|
+
Poll the encryption status of an uploaded file.
|
|
178
|
+
|
|
179
|
+
| Argument | Type | Description |
|
|
180
|
+
|------------|----------|-----------------------------------------------|
|
|
181
|
+
| `file_key` | `String` | The `file_key` from the `share_file` response |
|
|
182
|
+
|
|
183
|
+
Returns a `Konfidant::FileStatusResponse`:
|
|
184
|
+
|
|
185
|
+
| Field | Type | When set |
|
|
186
|
+
|----------------|-----------|-----------------------------------------|
|
|
187
|
+
| `status` | `String` | Always (`"processing"` or `"complete"`) |
|
|
188
|
+
| `message` | `String` | When `processing` |
|
|
189
|
+
| `file_id` | `String` | When `complete` |
|
|
190
|
+
| `file_name` | `String` | When `complete` |
|
|
191
|
+
| `share_url` | `String` | When `complete` |
|
|
192
|
+
| `expires_at` | `String` | When `complete` |
|
|
193
|
+
| `verified_burn`| `Boolean` | When `complete` |
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### `client.list_shares(type: nil, status: nil, limit: nil, offset: nil)`
|
|
198
|
+
|
|
199
|
+
List all shares for the authenticated organization. All parameters are optional.
|
|
200
|
+
|
|
201
|
+
| Argument | Type | Description |
|
|
202
|
+
|----------|----------|--------------------------------|
|
|
203
|
+
| `type` | `String` | `"file"` or `"text"` |
|
|
204
|
+
| `status` | `String` | `"active"` or `"accessed"` |
|
|
205
|
+
| `limit` | `Integer`| Page size (default 50) |
|
|
206
|
+
| `offset` | `Integer`| Pagination offset |
|
|
207
|
+
|
|
208
|
+
Returns a `Konfidant::ListSharesResponse`:
|
|
209
|
+
|
|
210
|
+
| Field | Type | Description |
|
|
211
|
+
|--------------|----------------------------|---------------------|
|
|
212
|
+
| `shares` | `Array<Konfidant::Share>` | Share entries |
|
|
213
|
+
| `pagination` | `Konfidant::Pagination` | Page metadata |
|
|
214
|
+
|
|
215
|
+
Each `Konfidant::Share`:
|
|
216
|
+
|
|
217
|
+
| Field | Type | Description |
|
|
218
|
+
|------------------|---------------|-------------------------|
|
|
219
|
+
| `type` | `String` | `"file"` or `"text"` |
|
|
220
|
+
| `file_name` | `String` | Filename or text label |
|
|
221
|
+
| `file_size_bytes`| `Integer` | Size in bytes |
|
|
222
|
+
| `created_at` | `String` | Creation datetime |
|
|
223
|
+
| `expires_at` | `String` | Expiry datetime |
|
|
224
|
+
| `accessed_at` | `String, nil` | Access datetime, or nil |
|
|
225
|
+
| `created_by` | `String` | Email of creator |
|
|
226
|
+
|
|
227
|
+
`Konfidant::Pagination`:
|
|
228
|
+
|
|
229
|
+
| Field | Type | Description |
|
|
230
|
+
|------------|-----------|--------------------------|
|
|
231
|
+
| `total` | `Integer` | Total number of shares |
|
|
232
|
+
| `limit` | `Integer` | Page size used |
|
|
233
|
+
| `offset` | `Integer` | Offset used |
|
|
234
|
+
| `has_more` | `Boolean` | Whether more pages exist |
|
|
235
|
+
|
|
236
|
+
#### Example: list shares
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
result = client.list_shares(type: 'file', limit: 10)
|
|
240
|
+
|
|
241
|
+
result.shares.each do |share|
|
|
242
|
+
puts "#{share.file_name} — expires #{share.expires_at}"
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
puts "Total: #{result.pagination.total}"
|
|
246
|
+
puts "More? #{result.pagination.has_more}"
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
---
|
|
250
|
+
|
|
251
|
+
### `client.share_and_upload_file`
|
|
252
|
+
|
|
253
|
+
Convenience wrapper that calls `share_file` → `upload_file` → polls `get_file_status` until complete.
|
|
254
|
+
|
|
255
|
+
```ruby
|
|
256
|
+
client.share_and_upload_file(
|
|
257
|
+
io: io,
|
|
258
|
+
size: size,
|
|
259
|
+
filename: filename,
|
|
260
|
+
content_type: content_type,
|
|
261
|
+
ttl_hours: ttl_hours,
|
|
262
|
+
poll_interval: 2, # seconds between status checks (default: 2)
|
|
263
|
+
timeout: 60 # max wait for encryption in seconds (default: 60)
|
|
264
|
+
)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
| Argument | Type | Default | Description |
|
|
268
|
+
|-----------------|-----------|---------|-------------------------------------|
|
|
269
|
+
| `io` | `IO` | — | Readable IO object (File, StringIO) |
|
|
270
|
+
| `size` | `Integer` | — | File size in bytes |
|
|
271
|
+
| `filename` | `String` | — | Filename with extension |
|
|
272
|
+
| `content_type` | `String` | — | MIME type |
|
|
273
|
+
| `ttl_hours` | `Integer` | — | Time-to-live in hours |
|
|
274
|
+
| `poll_interval` | `Numeric` | `2` | Seconds between status checks |
|
|
275
|
+
| `timeout` | `Numeric` | `60` | Max seconds to wait for encryption |
|
|
276
|
+
|
|
277
|
+
Returns a `Konfidant::ShareResult`:
|
|
278
|
+
|
|
279
|
+
| Field | Type | Description |
|
|
280
|
+
|----------------|-----------|--------------------------------|
|
|
281
|
+
| `share_url` | `String` | One-time download link |
|
|
282
|
+
| `file_id` | `String` | Unique file ID |
|
|
283
|
+
| `expires_at` | `String` | Expiry datetime |
|
|
284
|
+
| `verified_burn`| `Boolean` | Whether burn-on-read is active |
|
|
285
|
+
|
|
286
|
+
Raises `RuntimeError` with `"konfidant: encryption timed out after Ns"` if encryption does not complete
|
|
287
|
+
within `timeout` seconds.
|
|
288
|
+
|
|
289
|
+
#### Example: share and upload file
|
|
290
|
+
|
|
291
|
+
```ruby
|
|
292
|
+
file = File.open('confidential.zip', 'rb')
|
|
293
|
+
size = File.size('confidential.zip')
|
|
294
|
+
|
|
295
|
+
result = client.share_and_upload_file(
|
|
296
|
+
io: file,
|
|
297
|
+
size: size,
|
|
298
|
+
filename: 'confidential.zip',
|
|
299
|
+
content_type: 'application/zip',
|
|
300
|
+
ttl_hours: 48
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
puts "Ready to share: #{result.share_url}"
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
## Error handling
|
|
309
|
+
|
|
310
|
+
All API and S3 errors raise `Konfidant::ApiError`.
|
|
311
|
+
|
|
312
|
+
```ruby
|
|
313
|
+
begin
|
|
314
|
+
client.share_text(text: 'secret', ttl_hours: 1)
|
|
315
|
+
rescue Konfidant::ApiError => e
|
|
316
|
+
puts e.message # e.g. "Missing or invalid Authorization header."
|
|
317
|
+
puts e.status_code # e.g. 401
|
|
318
|
+
puts e.body.inspect # parsed response body (Hash or String)
|
|
319
|
+
end
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### Common error codes
|
|
323
|
+
|
|
324
|
+
| Status | Meaning |
|
|
325
|
+
|--------|----------------------------|
|
|
326
|
+
| `400` | Bad request / invalid body |
|
|
327
|
+
| `401` | Missing or invalid API key |
|
|
328
|
+
| `403` | Insufficient API key scope |
|
|
329
|
+
| `404` | Resource not found |
|
|
330
|
+
|
|
331
|
+
---
|
|
332
|
+
|
|
333
|
+
## Development
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
bundle install # install dependencies
|
|
337
|
+
bundle exec rspec # run tests
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Requirements
|
|
341
|
+
|
|
342
|
+
- Ruby >= 3.2.0
|
|
343
|
+
- No runtime dependencies (uses stdlib `net/http`, `uri`, `json`)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
require 'net/http'
|
|
2
|
+
require 'uri'
|
|
3
|
+
require 'json'
|
|
4
|
+
|
|
5
|
+
module Konfidant
|
|
6
|
+
DEFAULT_BASE_URL = 'https://www.konfidant.app'
|
|
7
|
+
DEFAULT_TIMEOUT = 120
|
|
8
|
+
|
|
9
|
+
class Client
|
|
10
|
+
def initialize(api_key:, base_url: nil, http_timeout: DEFAULT_TIMEOUT)
|
|
11
|
+
raise ArgumentError, 'api_key is required' if api_key.nil? || api_key.empty?
|
|
12
|
+
|
|
13
|
+
@api_key = api_key
|
|
14
|
+
@base_url = (base_url || DEFAULT_BASE_URL).sub(%r{/+\z}, '')
|
|
15
|
+
@http_timeout = http_timeout
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def share_text(text:, ttl_hours:)
|
|
19
|
+
body = request(:post, '/api/v1/texts', { text: text, ttl_hours: ttl_hours })
|
|
20
|
+
ShareTextResponse.new(
|
|
21
|
+
text_id: body['text_id'],
|
|
22
|
+
share_url: body['share_url'],
|
|
23
|
+
expires_at: body['expires_at'],
|
|
24
|
+
verified_burn: body['verified_burn']
|
|
25
|
+
)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def share_file(filename:, file_size:, ttl_hours:)
|
|
29
|
+
body = request(:post, '/api/v1/files', { filename: filename, file_size: file_size, ttl_hours: ttl_hours })
|
|
30
|
+
h = body['metadata_headers']
|
|
31
|
+
ShareFileResponse.new(
|
|
32
|
+
upload_url: body['upload_url'],
|
|
33
|
+
file_key: body['file_key'],
|
|
34
|
+
metadata_headers: FileMetadataHeaders.new(
|
|
35
|
+
user_id: h['x-amz-meta-user-id'],
|
|
36
|
+
ttl_hours: h['x-amz-meta-ttl-hours'],
|
|
37
|
+
organization_id: h['x-amz-meta-organization-id']
|
|
38
|
+
),
|
|
39
|
+
poll_url: body['poll_url']
|
|
40
|
+
)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def get_file_status(file_key)
|
|
44
|
+
encoded = encode_path_segment(file_key)
|
|
45
|
+
body = request(:get, "/api/v1/files/#{encoded}/status")
|
|
46
|
+
FileStatusResponse.new(
|
|
47
|
+
status: body['status'],
|
|
48
|
+
message: body['message'],
|
|
49
|
+
file_id: body['file_id'],
|
|
50
|
+
file_name: body['file_name'],
|
|
51
|
+
share_url: body['share_url'],
|
|
52
|
+
expires_at: body['expires_at'],
|
|
53
|
+
verified_burn: body['verified_burn']
|
|
54
|
+
)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def list_shares(type: nil, status: nil, limit: nil, offset: nil)
|
|
58
|
+
params = {}
|
|
59
|
+
params[:type] = type if type
|
|
60
|
+
params[:status] = status if status
|
|
61
|
+
params[:limit] = limit if limit
|
|
62
|
+
params[:offset] = offset if offset
|
|
63
|
+
|
|
64
|
+
path = '/api/v1/shares'
|
|
65
|
+
path += "?#{URI.encode_www_form(params)}" unless params.empty?
|
|
66
|
+
|
|
67
|
+
body = request(:get, path)
|
|
68
|
+
ListSharesResponse.new(
|
|
69
|
+
shares: body['shares'].map { |s| parse_share(s) },
|
|
70
|
+
pagination: parse_pagination(body['pagination'])
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def upload_file(io:, size:, content_type:, presigned:)
|
|
75
|
+
uri = URI.parse(presigned.upload_url)
|
|
76
|
+
http = build_http(uri)
|
|
77
|
+
|
|
78
|
+
req = Net::HTTP::Put.new(uri.request_uri)
|
|
79
|
+
req['Content-Type'] = content_type
|
|
80
|
+
req['Content-Length'] = size.to_s
|
|
81
|
+
req['x-amz-meta-organization-id'] = presigned.metadata_headers.organization_id
|
|
82
|
+
req['x-amz-meta-ttl-hours'] = presigned.metadata_headers.ttl_hours
|
|
83
|
+
req['x-amz-meta-user-id'] = presigned.metadata_headers.user_id
|
|
84
|
+
req.body_stream = io
|
|
85
|
+
|
|
86
|
+
resp = http.request(req)
|
|
87
|
+
return if resp.code.to_i.between?(200, 299)
|
|
88
|
+
|
|
89
|
+
raise ApiError.new("file upload failed: HTTP #{resp.code}", resp.code.to_i, resp.body)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def share_and_upload_file(io:, size:, filename:, content_type:, ttl_hours:,
|
|
93
|
+
poll_interval: 2, timeout: 60)
|
|
94
|
+
presigned = share_file(filename: filename, file_size: size, ttl_hours: ttl_hours)
|
|
95
|
+
upload_file(io: io, size: size, content_type: content_type, presigned: presigned)
|
|
96
|
+
|
|
97
|
+
deadline = Time.now + timeout
|
|
98
|
+
while Time.now < deadline
|
|
99
|
+
status = get_file_status(presigned.file_key)
|
|
100
|
+
if status.status == 'complete'
|
|
101
|
+
return ShareResult.new(
|
|
102
|
+
share_url: status.share_url,
|
|
103
|
+
file_id: status.file_id,
|
|
104
|
+
expires_at: status.expires_at,
|
|
105
|
+
verified_burn: status.verified_burn
|
|
106
|
+
)
|
|
107
|
+
end
|
|
108
|
+
sleep(poll_interval)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
raise "konfidant: encryption timed out after #{timeout}s"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
private
|
|
115
|
+
|
|
116
|
+
def auth_headers
|
|
117
|
+
{
|
|
118
|
+
'Authorization' => "Bearer #{@api_key}",
|
|
119
|
+
'Content-Type' => 'application/json'
|
|
120
|
+
}
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
def build_http(uri)
|
|
124
|
+
http = Net::HTTP.new(uri.host, uri.port)
|
|
125
|
+
http.use_ssl = uri.scheme == 'https'
|
|
126
|
+
unless @http_timeout.nil?
|
|
127
|
+
http.open_timeout = @http_timeout
|
|
128
|
+
http.read_timeout = @http_timeout
|
|
129
|
+
end
|
|
130
|
+
http
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def request(method, path, payload = nil)
|
|
134
|
+
uri = URI.parse("#{@base_url}#{path}")
|
|
135
|
+
http = build_http(uri)
|
|
136
|
+
|
|
137
|
+
req = case method
|
|
138
|
+
when :get then Net::HTTP::Get.new(uri.request_uri)
|
|
139
|
+
when :post then Net::HTTP::Post.new(uri.request_uri)
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
auth_headers.each { |k, v| req[k] = v }
|
|
143
|
+
req.body = JSON.generate(payload) if payload
|
|
144
|
+
|
|
145
|
+
resp = http.request(req)
|
|
146
|
+
content_type = resp['content-type'] || ''
|
|
147
|
+
|
|
148
|
+
body = if content_type.include?('application/json')
|
|
149
|
+
JSON.parse(resp.body)
|
|
150
|
+
else
|
|
151
|
+
resp.body
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
return body if resp.code.to_i.between?(200, 299)
|
|
155
|
+
|
|
156
|
+
message = body.is_a?(Hash) && body['error'] ? body['error'] : "HTTP #{resp.code}"
|
|
157
|
+
raise ApiError.new(message, resp.code.to_i, body)
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def encode_path_segment(str)
|
|
161
|
+
str.gsub(/[^A-Za-z0-9\-._~]/) { |c| c.bytes.map { |b| format('%%%02X', b) }.join }
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
def parse_share(s)
|
|
165
|
+
Share.new(
|
|
166
|
+
type: s['type'],
|
|
167
|
+
file_name: s['file_name'],
|
|
168
|
+
file_size_bytes: s['file_size_bytes'],
|
|
169
|
+
created_at: s['created_at'],
|
|
170
|
+
expires_at: s['expires_at'],
|
|
171
|
+
accessed_at: s['accessed_at'],
|
|
172
|
+
created_by: s['created_by']
|
|
173
|
+
)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def parse_pagination(p)
|
|
177
|
+
Pagination.new(
|
|
178
|
+
total: p['total'],
|
|
179
|
+
limit: p['limit'],
|
|
180
|
+
offset: p['offset'],
|
|
181
|
+
has_more: p['has_more']
|
|
182
|
+
)
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Konfidant
|
|
2
|
+
FileMetadataHeaders = Data.define(:user_id, :ttl_hours, :organization_id)
|
|
3
|
+
|
|
4
|
+
ShareTextResponse = Data.define(:text_id, :share_url, :expires_at, :verified_burn)
|
|
5
|
+
|
|
6
|
+
ShareFileResponse = Data.define(:upload_url, :file_key, :metadata_headers, :poll_url)
|
|
7
|
+
|
|
8
|
+
FileStatusResponse = Data.define(
|
|
9
|
+
:status, :message, :file_id, :file_name, :share_url, :expires_at, :verified_burn
|
|
10
|
+
) do
|
|
11
|
+
def initialize(status:, message: nil, file_id: nil, file_name: nil,
|
|
12
|
+
share_url: nil, expires_at: nil, verified_burn: nil)
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Share = Data.define(
|
|
18
|
+
:type, :file_name, :file_size_bytes, :created_at, :expires_at, :accessed_at, :created_by
|
|
19
|
+
) do
|
|
20
|
+
def initialize(type:, file_name:, file_size_bytes:, created_at:,
|
|
21
|
+
expires_at:, created_by:, accessed_at: nil)
|
|
22
|
+
super
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Pagination = Data.define(:total, :limit, :offset, :has_more)
|
|
27
|
+
|
|
28
|
+
ListSharesResponse = Data.define(:shares, :pagination)
|
|
29
|
+
|
|
30
|
+
ShareResult = Data.define(:share_url, :file_id, :expires_at, :verified_burn)
|
|
31
|
+
end
|
data/lib/konfidant.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: konfidant
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.7.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Konfidant
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: rake
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '13.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '13.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rspec
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '3.13'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '3.13'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: webmock
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.23'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.23'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: simplecov
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '0.22'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0.22'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: simplecov-lcov
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '0.8'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '0.8'
|
|
82
|
+
email:
|
|
83
|
+
- hello@konfidant.app
|
|
84
|
+
executables: []
|
|
85
|
+
extensions: []
|
|
86
|
+
extra_rdoc_files: []
|
|
87
|
+
files:
|
|
88
|
+
- README.md
|
|
89
|
+
- lib/konfidant.rb
|
|
90
|
+
- lib/konfidant/client.rb
|
|
91
|
+
- lib/konfidant/errors.rb
|
|
92
|
+
- lib/konfidant/types.rb
|
|
93
|
+
- lib/konfidant/version.rb
|
|
94
|
+
homepage: https://github.com/konfidant/sdk-ruby
|
|
95
|
+
licenses:
|
|
96
|
+
- MIT
|
|
97
|
+
metadata: {}
|
|
98
|
+
rdoc_options: []
|
|
99
|
+
require_paths:
|
|
100
|
+
- lib
|
|
101
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
102
|
+
requirements:
|
|
103
|
+
- - ">="
|
|
104
|
+
- !ruby/object:Gem::Version
|
|
105
|
+
version: 3.2.0
|
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
requirements: []
|
|
112
|
+
rubygems_version: 4.0.10
|
|
113
|
+
specification_version: 4
|
|
114
|
+
summary: Official Ruby SDK for the Konfidant API
|
|
115
|
+
test_files: []
|