keeper_secrets_manager 17.0.4 → 17.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/CHANGELOG.md +100 -16
- data/Gemfile +6 -3
- data/README.md +75 -270
- data/Rakefile +1 -1
- data/bin/console +47 -0
- data/keeper_secrets_manager.gemspec +36 -0
- data/lib/keeper_secrets_manager/cache.rb +139 -0
- data/lib/keeper_secrets_manager/config_keys.rb +4 -2
- data/lib/keeper_secrets_manager/core.rb +993 -452
- data/lib/keeper_secrets_manager/crypto.rb +101 -116
- data/lib/keeper_secrets_manager/dto/payload.rb +7 -6
- data/lib/keeper_secrets_manager/dto.rb +374 -38
- data/lib/keeper_secrets_manager/errors.rb +13 -2
- data/lib/keeper_secrets_manager/field_types.rb +3 -3
- data/lib/keeper_secrets_manager/folder_manager.rb +25 -29
- data/lib/keeper_secrets_manager/keeper_globals.rb +11 -17
- data/lib/keeper_secrets_manager/notation.rb +202 -93
- data/lib/keeper_secrets_manager/notation_enhancements.rb +24 -24
- data/lib/keeper_secrets_manager/storage.rb +38 -38
- data/lib/keeper_secrets_manager/totp.rb +27 -27
- data/lib/keeper_secrets_manager/utils.rb +85 -18
- data/lib/keeper_secrets_manager/version.rb +2 -2
- data/lib/keeper_secrets_manager.rb +11 -3
- metadata +39 -22
- data/DEVELOPER_SETUP.md +0 -0
- data/MANUAL_TESTING_GUIDE.md +0 -332
- data/RUBY_SDK_COMPLETE_DOCUMENTATION.md +0 -354
- data/RUBY_SDK_COMPREHENSIVE_SUMMARY.md +0 -192
- data/examples/01_quick_start.rb +0 -45
- data/examples/02_authentication.rb +0 -82
- data/examples/03_retrieve_secrets.rb +0 -81
- data/examples/04_create_update_delete.rb +0 -104
- data/examples/05_field_types.rb +0 -135
- data/examples/06_files.rb +0 -137
- data/examples/07_folders.rb +0 -145
- data/examples/08_notation.rb +0 -103
- data/examples/09_totp.rb +0 -100
- data/examples/README.md +0 -89
|
@@ -1,354 +0,0 @@
|
|
|
1
|
-
# Ruby SDK Complete Documentation - Keeper Secrets Manager
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
|
|
5
|
-
The Ruby SDK for Keeper Secrets Manager is a feature-complete implementation that provides secure access to secrets, records, and files stored in Keeper vaults. This document consolidates all technical documentation, implementation details, and status reports.
|
|
6
|
-
|
|
7
|
-
## Implementation Status: ✅ COMPLETE
|
|
8
|
-
|
|
9
|
-
### Core Features Implemented
|
|
10
|
-
- **Authentication & Configuration**: Token-based auth with ECDSA signatures and AES-GCM encryption
|
|
11
|
-
- **Secrets Management**: Full CRUD operations for records with batch support
|
|
12
|
-
- **File Operations**: Binary-safe upload/download with encryption (tested up to 5MB+)
|
|
13
|
-
- **Notation System**: Complete keeper:// URI parsing with all selector types
|
|
14
|
-
- **Folder Operations**: Full folder management (create, update, delete, list)
|
|
15
|
-
- **TOTP Support**: RFC 6238 compliant with SHA1/256/512 algorithms
|
|
16
|
-
- **Caching**: In-memory cache with TTL management
|
|
17
|
-
- **Testing**: Comprehensive test suite with mock mode for offline testing
|
|
18
|
-
|
|
19
|
-
### Technical Architecture
|
|
20
|
-
|
|
21
|
-
#### Design Philosophy
|
|
22
|
-
- **Dynamic DTOs**: JavaScript-style flexible records using Ruby's method_missing
|
|
23
|
-
- **Minimal Dependencies**: Mostly stdlib with FedRAMP compliance in mind
|
|
24
|
-
- **Ruby Idiomatic**: Snake_case methods, keyword arguments, blocks where appropriate
|
|
25
|
-
- **Runtime Validation**: No rigid type constraints, flexible field handling
|
|
26
|
-
|
|
27
|
-
#### Project Structure
|
|
28
|
-
```
|
|
29
|
-
sdk/ruby/
|
|
30
|
-
├── lib/keeper_secrets_manager/
|
|
31
|
-
│ ├── core.rb # Main SDK logic and client
|
|
32
|
-
│ ├── crypto.rb # Encryption/decryption operations
|
|
33
|
-
│ ├── storage.rb # Storage backends (memory, file)
|
|
34
|
-
│ ├── notation.rb # Keeper URI notation parser
|
|
35
|
-
│ ├── dto.rb # Dynamic data transfer objects
|
|
36
|
-
│ ├── totp.rb # TOTP implementation
|
|
37
|
-
│ └── version.rb # Version management
|
|
38
|
-
├── spec/ # RSpec unit tests
|
|
39
|
-
├── test/integration/ # Integration tests
|
|
40
|
-
├── examples/ # Usage examples
|
|
41
|
-
└── README.md # User documentation
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
## Key Implementation Details
|
|
45
|
-
|
|
46
|
-
### Authentication Flow
|
|
47
|
-
1. Parse one-time token (format: `REGION:BASE64_TOKEN`)
|
|
48
|
-
2. Exchange token for encrypted app key via `/get_client_params`
|
|
49
|
-
3. Generate EC key pair and sign request with ECDSA
|
|
50
|
-
4. Store derived keys in configured storage backend
|
|
51
|
-
|
|
52
|
-
### Encryption Specifications
|
|
53
|
-
- **Record Encryption**: AES-GCM with 256-bit keys
|
|
54
|
-
- **Key Exchange**: RSA-OAEP for app key encryption
|
|
55
|
-
- **Request Signing**: ECDSA with P-256 curve
|
|
56
|
-
- **HMAC**: SHA512 for request authentication
|
|
57
|
-
- **Nonce**: 96-bit random for AES-GCM
|
|
58
|
-
|
|
59
|
-
### Dynamic Record Structure
|
|
60
|
-
```ruby
|
|
61
|
-
# Flexible field access without rigid types
|
|
62
|
-
record = KeeperRecord.new(
|
|
63
|
-
title: 'My Login',
|
|
64
|
-
fields: [
|
|
65
|
-
{ 'type' => 'login', 'value' => ['username'] },
|
|
66
|
-
{ 'type' => 'password', 'value' => ['pass123'] }
|
|
67
|
-
]
|
|
68
|
-
)
|
|
69
|
-
|
|
70
|
-
# Dynamic accessors
|
|
71
|
-
record.login = 'new_username'
|
|
72
|
-
puts record.password # => 'pass123'
|
|
73
|
-
|
|
74
|
-
# Complex fields
|
|
75
|
-
record.set_field('name', {
|
|
76
|
-
'first' => 'John',
|
|
77
|
-
'middle' => 'Q',
|
|
78
|
-
'last' => 'Doe'
|
|
79
|
-
})
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
### Notation System Examples
|
|
83
|
-
```ruby
|
|
84
|
-
# Basic selectors
|
|
85
|
-
"keeper://RECORD_UID/type" # Record type
|
|
86
|
-
"keeper://RECORD_UID/title" # Record title
|
|
87
|
-
"keeper://RECORD_UID/notes" # Record notes
|
|
88
|
-
|
|
89
|
-
# Field selectors
|
|
90
|
-
"keeper://RECORD_UID/field/password" # Password field
|
|
91
|
-
"keeper://RECORD_UID/field/name[0][first]" # First name
|
|
92
|
-
"keeper://RECORD_UID/custom_field/API Key" # Custom field
|
|
93
|
-
|
|
94
|
-
# File operations
|
|
95
|
-
"keeper://RECORD_UID/file/document.pdf" # File by name
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Ruby Version Requirements
|
|
99
|
-
|
|
100
|
-
- **Minimum**: Ruby 2.7+ (for AES-GCM support)
|
|
101
|
-
- **Tested**: Ruby 2.7, 3.0, 3.1, 3.2, 3.3, latest
|
|
102
|
-
- **Note**: Requires OpenSSL 1.1.0+ for full functionality
|
|
103
|
-
|
|
104
|
-
## Testing Infrastructure
|
|
105
|
-
|
|
106
|
-
### Unit Tests (RSpec)
|
|
107
|
-
- 19 comprehensive tests covering all components
|
|
108
|
-
- 100% pass rate
|
|
109
|
-
- Tests for DTOs, storage, crypto, notation, utils
|
|
110
|
-
|
|
111
|
-
### Integration Tests
|
|
112
|
-
- Mock mode for offline testing without credentials
|
|
113
|
-
- Docker test suite for multi-version testing
|
|
114
|
-
- Performance benchmarks
|
|
115
|
-
- Error handling scenarios
|
|
116
|
-
|
|
117
|
-
### Mock Mode
|
|
118
|
-
```ruby
|
|
119
|
-
# Automatically enabled when no config.base64 exists
|
|
120
|
-
ENV['KEEPER_MOCK_MODE'] = 'true'
|
|
121
|
-
|
|
122
|
-
# Works with all operations
|
|
123
|
-
secrets = sm.get_secrets() # Returns mock data
|
|
124
|
-
sm.create_secret(record) # Simulates creation
|
|
125
|
-
```
|
|
126
|
-
|
|
127
|
-
### Folder and Record Structure
|
|
128
|
-
The SDK returns a flat list of all records, including those stored in folders:
|
|
129
|
-
- `response.records` contains ALL records (root + folder records)
|
|
130
|
-
- Each record has `folder_uid` property indicating its parent folder
|
|
131
|
-
- `response.folders` contains folder objects with their own `records` arrays
|
|
132
|
-
- This matches the behavior of Python, JavaScript, and Java SDKs
|
|
133
|
-
|
|
134
|
-
### Folder Hierarchy Management
|
|
135
|
-
The SDK provides advanced folder hierarchy functionality:
|
|
136
|
-
|
|
137
|
-
```ruby
|
|
138
|
-
# Get all folders
|
|
139
|
-
folders = client.get_folders
|
|
140
|
-
|
|
141
|
-
# Get folder manager for advanced operations
|
|
142
|
-
fm = client.folder_manager
|
|
143
|
-
|
|
144
|
-
# Build folder tree
|
|
145
|
-
tree = fm.build_folder_tree
|
|
146
|
-
|
|
147
|
-
# Get folder path
|
|
148
|
-
path = client.get_folder_path(folder_uid) # Returns "Parent/Child/Grandchild"
|
|
149
|
-
|
|
150
|
-
# Find folder by name
|
|
151
|
-
folder = client.find_folder_by_name("Finance")
|
|
152
|
-
folder = client.find_folder_by_name("Finance", parent_uid: "parent_uid")
|
|
153
|
-
|
|
154
|
-
# Get folder relationships
|
|
155
|
-
ancestors = fm.get_ancestors(folder_uid) # Returns parent, grandparent, etc.
|
|
156
|
-
descendants = fm.get_descendants(folder_uid) # Returns children, grandchildren, etc.
|
|
157
|
-
|
|
158
|
-
# Print folder tree
|
|
159
|
-
fm.print_tree # Displays hierarchical structure with records
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
## Critical Implementation Details for Future SDKs
|
|
163
|
-
|
|
164
|
-
### One-Time Token Authentication (CRITICAL)
|
|
165
|
-
When implementing token authentication in new SDKs, the following steps are MANDATORY:
|
|
166
|
-
|
|
167
|
-
1. **Token Parsing**: Extract region/hostname from format `[REGION:]TOKEN`
|
|
168
|
-
- Regions: US, EU, AU, GOV, JP, CA map to specific hostnames
|
|
169
|
-
- Legacy format: Just the token without region prefix
|
|
170
|
-
|
|
171
|
-
2. **Client ID Generation** (Often missed!):
|
|
172
|
-
```
|
|
173
|
-
client_id = Base64(HMAC-SHA512(token_bytes, "KEEPER_SECRETS_MANAGER_CLIENT_ID"))
|
|
174
|
-
```
|
|
175
|
-
- The token is NEVER sent directly to the server
|
|
176
|
-
- Only the hashed client_id is transmitted
|
|
177
|
-
|
|
178
|
-
3. **Initial Authentication Request**:
|
|
179
|
-
- Send: client_id, public_key, client_version
|
|
180
|
-
- Receive: encryptedAppKey, appOwnerPublicKey
|
|
181
|
-
|
|
182
|
-
4. **App Key Decryption**:
|
|
183
|
-
- Use AES-GCM decryption with the original token as the key
|
|
184
|
-
- NOT EC/RSA decryption (common mistake)
|
|
185
|
-
```
|
|
186
|
-
app_key = AES_DECRYPT(encryptedAppKey, token_bytes)
|
|
187
|
-
```
|
|
188
|
-
|
|
189
|
-
5. **Cleanup**: Delete the token from storage after successful binding
|
|
190
|
-
|
|
191
|
-
### OpenSSL 3.0 Compatibility
|
|
192
|
-
For EC key creation in OpenSSL 3.0+, use ASN.1 DER format instead of direct private key assignment:
|
|
193
|
-
```ruby
|
|
194
|
-
# Create ASN1 sequence
|
|
195
|
-
asn1 = OpenSSL::ASN1::Sequence([
|
|
196
|
-
OpenSSL::ASN1::Integer(1),
|
|
197
|
-
OpenSSL::ASN1::OctetString(private_key_bytes),
|
|
198
|
-
OpenSSL::ASN1::ObjectId('prime256v1', 0, :EXPLICIT),
|
|
199
|
-
OpenSSL::ASN1::BitString(public_key_bytes, 1, :EXPLICIT)
|
|
200
|
-
])
|
|
201
|
-
key = OpenSSL::PKey::EC.new(asn1.to_der)
|
|
202
|
-
```
|
|
203
|
-
|
|
204
|
-
### Server Public Key ID Selection
|
|
205
|
-
Different SDKs use different default key IDs:
|
|
206
|
-
- **Python**: Default key ID `"10"`
|
|
207
|
-
- **Java/JavaScript**: Default key ID `7`
|
|
208
|
-
- **Ruby**: Should use `7` as default
|
|
209
|
-
|
|
210
|
-
The server will respond with `"invalid key id"` error if wrong key is used, and will specify the correct key ID to retry with. The SDK must handle this retry automatically.
|
|
211
|
-
|
|
212
|
-
## Known Issues and Pending Work
|
|
213
|
-
|
|
214
|
-
### ⚠️ CRITICAL: Client Version Registration
|
|
215
|
-
- **Current**: Using 'mr' prefix (from Rust SDK) as temporary workaround
|
|
216
|
-
- **Required**: Register 'mb' prefix with Keeper servers
|
|
217
|
-
- **Action**: Update CLIENT_VERSION_PREFIX before production release
|
|
218
|
-
|
|
219
|
-
### Not Implemented (Low Priority)
|
|
220
|
-
1. **Proxy Support**: Not implemented (can be added later)
|
|
221
|
-
2. **Async Operations**: Synchronous only (matches most SDKs)
|
|
222
|
-
3. **Advanced Search**: Server-side search (client filtering available)
|
|
223
|
-
4. **True Batch Operations**: Sequential implementation exists
|
|
224
|
-
|
|
225
|
-
## API Reference
|
|
226
|
-
|
|
227
|
-
### Initialization
|
|
228
|
-
```ruby
|
|
229
|
-
require 'keeper_secrets_manager'
|
|
230
|
-
|
|
231
|
-
# From token
|
|
232
|
-
sm = KeeperSecretsManager::SecretsManager.new(
|
|
233
|
-
token: 'US:ONE_TIME_TOKEN_HERE'
|
|
234
|
-
)
|
|
235
|
-
|
|
236
|
-
# From config
|
|
237
|
-
sm = KeeperSecretsManager::SecretsManager.new(
|
|
238
|
-
config: KeeperSecretsManager::FileStorage.new('ksm-config.json')
|
|
239
|
-
)
|
|
240
|
-
```
|
|
241
|
-
|
|
242
|
-
### Secret Operations
|
|
243
|
-
```ruby
|
|
244
|
-
# List all secrets
|
|
245
|
-
secrets = sm.get_secrets()
|
|
246
|
-
|
|
247
|
-
# Get by UID
|
|
248
|
-
secret = sm.get_secrets(['RECORD_UID'])[0]
|
|
249
|
-
|
|
250
|
-
# Create new
|
|
251
|
-
record = sm.create_secret(
|
|
252
|
-
KeeperSecretsManager::Dto::KeeperRecord.new(
|
|
253
|
-
title: 'New Secret',
|
|
254
|
-
fields: [...]
|
|
255
|
-
)
|
|
256
|
-
)
|
|
257
|
-
|
|
258
|
-
# Update
|
|
259
|
-
sm.update_secret(record)
|
|
260
|
-
|
|
261
|
-
# Delete
|
|
262
|
-
sm.delete_secret(['RECORD_UID'])
|
|
263
|
-
```
|
|
264
|
-
|
|
265
|
-
### File Operations
|
|
266
|
-
```ruby
|
|
267
|
-
# Upload
|
|
268
|
-
file_uid = sm.upload_file(
|
|
269
|
-
'RECORD_UID',
|
|
270
|
-
'document.pdf',
|
|
271
|
-
File.read('path/to/document.pdf', mode: 'rb')
|
|
272
|
-
)
|
|
273
|
-
|
|
274
|
-
# Download
|
|
275
|
-
file_data = sm.download_file('FILE_UID')
|
|
276
|
-
File.write('output.pdf', file_data['data'], mode: 'wb')
|
|
277
|
-
```
|
|
278
|
-
|
|
279
|
-
### Folder Operations
|
|
280
|
-
```ruby
|
|
281
|
-
# List folders
|
|
282
|
-
folders = sm.get_folders()
|
|
283
|
-
|
|
284
|
-
# Create folder
|
|
285
|
-
folder_uid = sm.create_folder('New Folder', parent_uid: 'PARENT_UID')
|
|
286
|
-
|
|
287
|
-
# Update folder
|
|
288
|
-
sm.update_folder('FOLDER_UID', 'Updated Name')
|
|
289
|
-
|
|
290
|
-
# Delete folder
|
|
291
|
-
sm.delete_folder('FOLDER_UID', force: true)
|
|
292
|
-
```
|
|
293
|
-
|
|
294
|
-
### TOTP Support
|
|
295
|
-
```ruby
|
|
296
|
-
# Get TOTP URL via notation
|
|
297
|
-
totp_url = sm.get_notation("keeper://RECORD/field/oneTimeCode")
|
|
298
|
-
|
|
299
|
-
# Generate code
|
|
300
|
-
params = KeeperSecretsManager::TOTP.parse_url(totp_url)
|
|
301
|
-
code = KeeperSecretsManager::TOTP.generate_code(params['secret'])
|
|
302
|
-
```
|
|
303
|
-
|
|
304
|
-
## Security Considerations
|
|
305
|
-
|
|
306
|
-
1. **Key Storage**: Never commit keys or tokens to version control
|
|
307
|
-
2. **Memory Security**: Sensitive data cleared after use where possible
|
|
308
|
-
3. **Transport Security**: All API calls use HTTPS with certificate validation
|
|
309
|
-
4. **Encryption**: All data encrypted at rest and in transit
|
|
310
|
-
5. **Authentication**: Each request signed with ECDSA
|
|
311
|
-
|
|
312
|
-
## Performance Notes
|
|
313
|
-
|
|
314
|
-
- File operations optimized for streaming (5MB+ tested)
|
|
315
|
-
- In-memory caching reduces API calls
|
|
316
|
-
- Lazy loading of record details
|
|
317
|
-
- Efficient batch operations
|
|
318
|
-
|
|
319
|
-
## Publishing Checklist
|
|
320
|
-
|
|
321
|
-
1. ✅ Complete implementation of all features
|
|
322
|
-
2. ✅ Comprehensive test coverage
|
|
323
|
-
3. ✅ Documentation and examples
|
|
324
|
-
4. ⚠️ Register 'mb' client version with Keeper
|
|
325
|
-
5. ⚠️ Update CLIENT_VERSION_PREFIX to 'mb'
|
|
326
|
-
6. ⚠️ Publish to RubyGems.org
|
|
327
|
-
|
|
328
|
-
## Support and Resources
|
|
329
|
-
|
|
330
|
-
- **Documentation**: See README.md for usage guide
|
|
331
|
-
- **Examples**: Check examples/ directory for code samples
|
|
332
|
-
- **Tests**: Run `rspec` for unit tests
|
|
333
|
-
- **Integration**: See test/integration/ for API tests
|
|
334
|
-
|
|
335
|
-
## Recent Fixes and Improvements
|
|
336
|
-
|
|
337
|
-
### Token Authentication Implementation (Fixed)
|
|
338
|
-
The Ruby SDK now correctly implements one-time token authentication:
|
|
339
|
-
1. **Client ID Generation**: Properly hashes token using HMAC-SHA512
|
|
340
|
-
2. **App Key Decryption**: Uses AES-GCM with token as key (not EC decryption)
|
|
341
|
-
3. **OpenSSL 3.0 Support**: Uses ASN.1 DER format for EC key creation
|
|
342
|
-
4. **Server Key ID**: Defaults to "7" with automatic retry on key errors
|
|
343
|
-
5. **Error Handling**: Fixed config access during token binding
|
|
344
|
-
|
|
345
|
-
See `TOKEN_AUTH_FIXES.md` for complete details.
|
|
346
|
-
|
|
347
|
-
## Summary
|
|
348
|
-
|
|
349
|
-
The Ruby SDK for Keeper Secrets Manager is feature-complete and ready for production use after client version registration. It maintains compatibility with existing Keeper SDKs while providing a Ruby-idiomatic interface with flexible, dynamic record handling as requested.
|
|
350
|
-
|
|
351
|
-
---
|
|
352
|
-
|
|
353
|
-
*Last Updated: SDK Version 1.0.0*
|
|
354
|
-
*Ruby 2.7+ Required | Full API Compatibility*
|
|
@@ -1,192 +0,0 @@
|
|
|
1
|
-
# Keeper Secrets Manager Ruby SDK - Comprehensive Summary
|
|
2
|
-
|
|
3
|
-
## Overview
|
|
4
|
-
The Keeper Secrets Manager Ruby SDK is a fully-featured implementation that provides secure access to Keeper's secrets management platform. The SDK follows the architectural patterns established by other Keeper SDKs while embracing Ruby idioms and conventions.
|
|
5
|
-
|
|
6
|
-
## Current Implementation Status
|
|
7
|
-
|
|
8
|
-
### ✅ Fully Implemented Features
|
|
9
|
-
|
|
10
|
-
#### 1. **Core Authentication & Configuration**
|
|
11
|
-
- Token-based authentication with ECDSA signatures
|
|
12
|
-
- AES-GCM encryption (requires Ruby 2.7+ or OpenSSL 1.1.0+)
|
|
13
|
-
- Multiple storage backends (In-memory, File-based)
|
|
14
|
-
- Environment variable support
|
|
15
|
-
- Configuration management with validation
|
|
16
|
-
|
|
17
|
-
#### 2. **Secrets Management**
|
|
18
|
-
- **Read Operations**: Get secrets by UID, list all secrets, search by title
|
|
19
|
-
- **Write Operations**: Create, update, and delete secrets (fully implemented)
|
|
20
|
-
- **Batch Operations**: Delete multiple secrets in one call
|
|
21
|
-
- **Caching**: In-memory caching with TTL management
|
|
22
|
-
|
|
23
|
-
#### 3. **File Operations**
|
|
24
|
-
- **Upload**: Fully implemented with AES-GCM encryption
|
|
25
|
-
- Supports binary and text files
|
|
26
|
-
- Tested with files up to 5MB+
|
|
27
|
-
- Multiple files per record
|
|
28
|
-
- **Download**: Complete implementation
|
|
29
|
-
- Retrieves encrypted files from server
|
|
30
|
-
- Decrypts using AES-GCM
|
|
31
|
-
- Returns file metadata and content
|
|
32
|
-
|
|
33
|
-
#### 4. **Keeper Notation System**
|
|
34
|
-
Complete implementation of the keeper:// URI notation:
|
|
35
|
-
- Basic selectors: `type`, `title`, `notes`
|
|
36
|
-
- Field access: `field/TYPE[INDEX]`
|
|
37
|
-
- Custom fields: `custom_field/LABEL`
|
|
38
|
-
- File references: `file/FILENAME` or `file/FILE_UID`
|
|
39
|
-
- Complex field properties: `name[0][first]`
|
|
40
|
-
- Escape sequences and Base64 encoding support
|
|
41
|
-
|
|
42
|
-
#### 5. **Folder Operations**
|
|
43
|
-
Full CRUD support for folders:
|
|
44
|
-
- List all folders
|
|
45
|
-
- Create folders (with parent hierarchy)
|
|
46
|
-
- Update folder names
|
|
47
|
-
- Delete folders (with force option for non-empty)
|
|
48
|
-
- Create records within specific folders
|
|
49
|
-
|
|
50
|
-
#### 6. **TOTP Support**
|
|
51
|
-
Comprehensive Time-based One-Time Password implementation:
|
|
52
|
-
- Generate TOTP codes (RFC 6238 compliant)
|
|
53
|
-
- Parse otpauth:// URLs
|
|
54
|
-
- Support for SHA1, SHA256, SHA512 algorithms
|
|
55
|
-
- 6 or 8 digit codes
|
|
56
|
-
- Configurable time windows
|
|
57
|
-
- Code validation with drift tolerance
|
|
58
|
-
|
|
59
|
-
### 🏗️ Technical Architecture
|
|
60
|
-
|
|
61
|
-
#### 1. **Dynamic DTO Structure**
|
|
62
|
-
JavaScript-style flexible records using Ruby's dynamic features:
|
|
63
|
-
- Hash-based initialization
|
|
64
|
-
- Fields stored as arrays of hashes
|
|
65
|
-
- Dynamic field access via `method_missing`
|
|
66
|
-
- No rigid type constraints
|
|
67
|
-
- Runtime validation
|
|
68
|
-
|
|
69
|
-
#### 2. **Project Structure**
|
|
70
|
-
```
|
|
71
|
-
sdk/ruby/
|
|
72
|
-
├── lib/ # Core SDK implementation
|
|
73
|
-
│ └── keeper_secrets_manager/
|
|
74
|
-
│ ├── core.rb # Main SecretsManager class
|
|
75
|
-
│ ├── crypto.rb # Cryptographic operations
|
|
76
|
-
│ ├── storage.rb # Storage implementations
|
|
77
|
-
│ ├── notation.rb # Notation parser
|
|
78
|
-
│ ├── records.rb # Record models
|
|
79
|
-
│ ├── totp.rb # TOTP implementation
|
|
80
|
-
│ └── errors.rb # Error classes
|
|
81
|
-
├── spec/ # RSpec unit tests
|
|
82
|
-
├── test/ # Integration tests
|
|
83
|
-
├── examples/ # Usage examples
|
|
84
|
-
└── README.md # Documentation
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
#### 3. **Dependencies**
|
|
88
|
-
Minimal external dependencies:
|
|
89
|
-
- `openssl` (standard library)
|
|
90
|
-
- `json` (standard library)
|
|
91
|
-
- `base64` (standard library)
|
|
92
|
-
- `net/http` (standard library)
|
|
93
|
-
- `base32` gem (for TOTP support)
|
|
94
|
-
|
|
95
|
-
### 🧪 Testing Status
|
|
96
|
-
|
|
97
|
-
#### Unit Tests
|
|
98
|
-
- 19 comprehensive unit tests
|
|
99
|
-
- 100% pass rate
|
|
100
|
-
- Coverage includes:
|
|
101
|
-
- DTO operations
|
|
102
|
-
- Field manipulations
|
|
103
|
-
- Storage implementations
|
|
104
|
-
- Notation parsing
|
|
105
|
-
- Cryptographic operations
|
|
106
|
-
|
|
107
|
-
#### Integration Tests
|
|
108
|
-
- Mock mode for offline testing
|
|
109
|
-
- Full mock infrastructure for all operations
|
|
110
|
-
- Real API testing requires Ruby 2.7+
|
|
111
|
-
- Test scripts for CRUD operations
|
|
112
|
-
|
|
113
|
-
### ⚠️ Known Issues and Pending Work
|
|
114
|
-
|
|
115
|
-
#### 1. **Client Version Registration**
|
|
116
|
-
- **Current**: Using 'mr' prefix (borrowed from Rust SDK)
|
|
117
|
-
- **Required**: Register 'mb' prefix with Keeper servers
|
|
118
|
-
- **Impact**: Must be resolved before production release
|
|
119
|
-
|
|
120
|
-
#### 2. **Not Yet Implemented**
|
|
121
|
-
- Advanced server-side search (client-side filtering available)
|
|
122
|
-
- True batch create/update operations (sequential implementation exists)
|
|
123
|
-
- Additional storage backends (Redis, Database)
|
|
124
|
-
- Proxy support (can be added later)
|
|
125
|
-
- Async operations (synchronous only)
|
|
126
|
-
|
|
127
|
-
#### 3. **Platform Requirements**
|
|
128
|
-
- Ruby 2.7+ required for full functionality (AES-GCM support)
|
|
129
|
-
- Write operations require records to be in folders (Keeper platform requirement)
|
|
130
|
-
|
|
131
|
-
### 📊 Comparison with Other SDKs
|
|
132
|
-
|
|
133
|
-
The Ruby SDK achieves feature parity with other Keeper SDKs:
|
|
134
|
-
|
|
135
|
-
| Feature | Ruby | Python | JavaScript | Java | Status |
|
|
136
|
-
|---------|------|--------|------------|------|--------|
|
|
137
|
-
| Core CRUD | ✅ | ✅ | ✅ | ✅ | Complete |
|
|
138
|
-
| File Operations | ✅ | ✅ | ✅ | ✅ | Complete |
|
|
139
|
-
| Notation System | ✅ | ✅ | ✅ | ✅ | Complete |
|
|
140
|
-
| Folder Operations | ✅ | ✅ | ✅ | ✅ | Complete |
|
|
141
|
-
| TOTP Support | ✅ | ✅ | ✅ | ✅ | Complete |
|
|
142
|
-
| Batch Operations | Partial | ✅ | ✅ | ✅ | Sequential only |
|
|
143
|
-
| Proxy Support | ❌ | ✅ | ❌ | ❌ | Not implemented |
|
|
144
|
-
|
|
145
|
-
### 🚀 Usage Examples
|
|
146
|
-
|
|
147
|
-
```ruby
|
|
148
|
-
# Initialize SDK
|
|
149
|
-
require 'keeper_secrets_manager'
|
|
150
|
-
|
|
151
|
-
storage = KeeperSecretsManager::LocalConfigStorage.new()
|
|
152
|
-
sm = KeeperSecretsManager::SecretsManager.new(storage: storage)
|
|
153
|
-
|
|
154
|
-
# Get secrets
|
|
155
|
-
secrets = sm.get_secrets()
|
|
156
|
-
secret = sm.get_secret_by_title('My Login')
|
|
157
|
-
|
|
158
|
-
# Use notation
|
|
159
|
-
password = sm.get_notation('keeper://My Login/field/password')
|
|
160
|
-
|
|
161
|
-
# TOTP
|
|
162
|
-
totp_url = sm.get_notation('keeper://My 2FA/field/oneTimeCode')
|
|
163
|
-
params = KeeperSecretsManager::TOTP.parse_url(totp_url)
|
|
164
|
-
code = KeeperSecretsManager::TOTP.generate_code(params['secret'])
|
|
165
|
-
|
|
166
|
-
# File operations
|
|
167
|
-
file_uid = sm.upload_file(record_uid, file_data, 'document.pdf')
|
|
168
|
-
downloaded = sm.download_file(file_uid)
|
|
169
|
-
File.write('output.pdf', downloaded['data'], mode: 'wb')
|
|
170
|
-
|
|
171
|
-
# Folder operations
|
|
172
|
-
folder_uid = sm.create_folder('New Folder')
|
|
173
|
-
sm.update_folder(folder_uid, 'Renamed Folder')
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
### 📝 Key Design Decisions
|
|
177
|
-
|
|
178
|
-
1. **Ruby Idioms**: Snake_case methods, keyword arguments, blocks where appropriate
|
|
179
|
-
2. **Flexible DTOs**: JavaScript-style dynamic records without rigid type constraints
|
|
180
|
-
3. **Minimal Dependencies**: FedRAMP-compliant, using mostly standard library
|
|
181
|
-
4. **Compatibility**: Targets Ruby 2.7+ for full functionality
|
|
182
|
-
5. **Testing**: RSpec for unit tests, custom integration test suite
|
|
183
|
-
|
|
184
|
-
### ✅ Summary
|
|
185
|
-
|
|
186
|
-
The Ruby SDK is **feature-complete and production-ready** (pending client version registration). It successfully implements all core Keeper Secrets Manager functionality while maintaining Ruby's expressive and developer-friendly style. The SDK matches the capabilities of other Keeper SDKs and has been thoroughly tested with comprehensive unit and integration test suites.
|
|
187
|
-
|
|
188
|
-
**Next Steps**:
|
|
189
|
-
1. Register 'mb' client version prefix with Keeper
|
|
190
|
-
2. Update CLIENT_VERSION_PREFIX constant
|
|
191
|
-
3. Publish to RubyGems.org
|
|
192
|
-
4. Monitor for user feedback on batch operations and search features
|
data/examples/01_quick_start.rb
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
# Quick Start Example - Getting started with Keeper Secrets Manager
|
|
4
|
-
# This example shows the simplest way to connect and retrieve secrets
|
|
5
|
-
|
|
6
|
-
require 'keeper_secrets_manager'
|
|
7
|
-
|
|
8
|
-
# Method 1: Using a one-time token (simplest)
|
|
9
|
-
# Get your token from: https://app.keeper-security.com/secrets-manager
|
|
10
|
-
begin
|
|
11
|
-
token = ENV['KSM_TOKEN'] || 'US:YOUR_ONE_TIME_TOKEN'
|
|
12
|
-
|
|
13
|
-
# Initialize SDK with token
|
|
14
|
-
secrets_manager = KeeperSecretsManager.from_token(token)
|
|
15
|
-
|
|
16
|
-
# Get all secrets
|
|
17
|
-
secrets = secrets_manager.get_secrets
|
|
18
|
-
|
|
19
|
-
puts "Found #{secrets.length} secrets:"
|
|
20
|
-
secrets.each do |secret|
|
|
21
|
-
puts " - #{secret.title} (#{secret.type})"
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
rescue => e
|
|
25
|
-
puts "Error: #{e.message}"
|
|
26
|
-
puts "Make sure to set KSM_TOKEN environment variable or replace with your token"
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
# Method 2: Using base64 configuration (for repeated use)
|
|
30
|
-
# After first connection, save your config for reuse
|
|
31
|
-
begin
|
|
32
|
-
config_base64 = ENV['KSM_CONFIG'] || 'YOUR_BASE64_CONFIG_STRING'
|
|
33
|
-
|
|
34
|
-
# Initialize with saved configuration
|
|
35
|
-
secrets_manager = KeeperSecretsManager.from_config(config_base64)
|
|
36
|
-
|
|
37
|
-
# Get specific secret by UID
|
|
38
|
-
secret = secrets_manager.get_secret_by_uid('RECORD_UID')
|
|
39
|
-
puts "\nSecret details:"
|
|
40
|
-
puts " Title: #{secret.title}"
|
|
41
|
-
puts " Login: #{secret.fields['login']}"
|
|
42
|
-
|
|
43
|
-
rescue => e
|
|
44
|
-
puts "Error: #{e.message}"
|
|
45
|
-
end
|
|
@@ -1,82 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env ruby
|
|
2
|
-
|
|
3
|
-
# Authentication Example - Different ways to authenticate
|
|
4
|
-
# Shows how to initialize the SDK and save credentials for reuse
|
|
5
|
-
|
|
6
|
-
require 'keeper_secrets_manager'
|
|
7
|
-
|
|
8
|
-
puts "=== Authentication Methods ==="
|
|
9
|
-
|
|
10
|
-
# Method 1: One-time token (first time setup)
|
|
11
|
-
puts "\n1. Using One-Time Token:"
|
|
12
|
-
begin
|
|
13
|
-
# Get token from Keeper Secrets Manager UI
|
|
14
|
-
token = ENV['KSM_TOKEN'] || 'US:YOUR_ONE_TIME_TOKEN'
|
|
15
|
-
|
|
16
|
-
# Option A: Direct use (credentials not saved)
|
|
17
|
-
sm = KeeperSecretsManager.from_token(token)
|
|
18
|
-
puts "✓ Connected with token"
|
|
19
|
-
|
|
20
|
-
# Option B: Save credentials for reuse
|
|
21
|
-
storage = KeeperSecretsManager::Storage::InMemoryStorage.new
|
|
22
|
-
sm = KeeperSecretsManager.new(token: token, config: storage)
|
|
23
|
-
|
|
24
|
-
# Get the configuration as base64 for future use
|
|
25
|
-
config_base64 = storage.to_base64
|
|
26
|
-
puts "✓ Configuration saved. Use this for future connections:"
|
|
27
|
-
puts " export KSM_CONFIG='#{config_base64}'"
|
|
28
|
-
|
|
29
|
-
rescue => e
|
|
30
|
-
puts "✗ Error: #{e.message}"
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Method 2: Base64 configuration (subsequent connections)
|
|
34
|
-
puts "\n2. Using Base64 Configuration:"
|
|
35
|
-
begin
|
|
36
|
-
# Use saved configuration from environment or file
|
|
37
|
-
config_base64 = ENV['KSM_CONFIG'] || 'YOUR_SAVED_BASE64_CONFIG'
|
|
38
|
-
|
|
39
|
-
# Initialize with configuration
|
|
40
|
-
sm = KeeperSecretsManager.from_config(config_base64)
|
|
41
|
-
|
|
42
|
-
# Test connection
|
|
43
|
-
secrets = sm.get_secrets
|
|
44
|
-
puts "✓ Connected with saved config, found #{secrets.length} secrets"
|
|
45
|
-
|
|
46
|
-
rescue => e
|
|
47
|
-
puts "✗ Error: #{e.message}"
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Method 3: Using configuration file
|
|
51
|
-
puts "\n3. Using Configuration File:"
|
|
52
|
-
begin
|
|
53
|
-
# Save configuration to a file
|
|
54
|
-
config_file = 'keeper-config.json'
|
|
55
|
-
|
|
56
|
-
# Initialize with file storage
|
|
57
|
-
sm = KeeperSecretsManager.from_file(config_file)
|
|
58
|
-
|
|
59
|
-
puts "✓ Connected using config file: #{config_file}"
|
|
60
|
-
|
|
61
|
-
rescue => e
|
|
62
|
-
puts "✗ Error: #{e.message}"
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Method 4: Using environment variables
|
|
66
|
-
puts "\n4. Using Environment Variables:"
|
|
67
|
-
begin
|
|
68
|
-
# Set these environment variables:
|
|
69
|
-
# export KSM_HOSTNAME=keepersecurity.com
|
|
70
|
-
# export KSM_CLIENT_ID=your-client-id
|
|
71
|
-
# export KSM_PRIVATE_KEY=your-private-key
|
|
72
|
-
# export KSM_APP_KEY=your-app-key
|
|
73
|
-
|
|
74
|
-
storage = KeeperSecretsManager::Storage::EnvironmentStorage.new('KSM_')
|
|
75
|
-
sm = KeeperSecretsManager.new(config: storage)
|
|
76
|
-
|
|
77
|
-
puts "✓ Connected using environment variables"
|
|
78
|
-
|
|
79
|
-
rescue => e
|
|
80
|
-
puts "✗ Error: #{e.message}"
|
|
81
|
-
puts " Make sure KSM_* environment variables are set"
|
|
82
|
-
end
|