keeper_secrets_manager 17.0.3 → 17.0.4
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/DEVELOPER_SETUP.md +0 -0
- data/MANUAL_TESTING_GUIDE.md +332 -0
- data/RUBY_SDK_COMPLETE_DOCUMENTATION.md +354 -0
- data/RUBY_SDK_COMPREHENSIVE_SUMMARY.md +192 -0
- data/examples/01_quick_start.rb +45 -0
- data/examples/02_authentication.rb +82 -0
- data/examples/03_retrieve_secrets.rb +81 -0
- data/examples/04_create_update_delete.rb +104 -0
- data/examples/05_field_types.rb +135 -0
- data/examples/06_files.rb +137 -0
- data/examples/07_folders.rb +145 -0
- data/examples/08_notation.rb +103 -0
- data/examples/09_totp.rb +100 -0
- data/examples/README.md +89 -0
- data/lib/keeper_secrets_manager/version.rb +1 -1
- metadata +15 -12
- data/examples/basic_usage.rb +0 -139
- data/examples/config_string_example.rb +0 -99
- data/examples/debug_secrets.rb +0 -84
- data/examples/demo_list_secrets.rb +0 -182
- data/examples/download_files.rb +0 -100
- data/examples/flexible_records_example.rb +0 -94
- data/examples/folder_hierarchy_demo.rb +0 -109
- data/examples/full_demo.rb +0 -176
- data/examples/my_test_standalone.rb +0 -176
- data/examples/simple_test.rb +0 -162
- data/examples/storage_examples.rb +0 -126
@@ -1,109 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require_relative '../lib/keeper_secrets_manager'
|
4
|
-
|
5
|
-
# This example demonstrates folder hierarchy functionality in the Ruby SDK
|
6
|
-
|
7
|
-
begin
|
8
|
-
# Initialize client (assumes KSM_CONFIG environment variable is set)
|
9
|
-
client = KeeperSecretsManager.new
|
10
|
-
|
11
|
-
puts "=== Folder Hierarchy Demo ==="
|
12
|
-
puts ""
|
13
|
-
|
14
|
-
# Get all folders
|
15
|
-
folders = client.get_folders
|
16
|
-
puts "Found #{folders.length} folders"
|
17
|
-
puts ""
|
18
|
-
|
19
|
-
# Get folder manager for advanced operations
|
20
|
-
fm = client.folder_manager
|
21
|
-
|
22
|
-
# Build and display folder tree
|
23
|
-
puts "Folder Tree Structure:"
|
24
|
-
puts "-" * 40
|
25
|
-
fm.print_tree
|
26
|
-
puts ""
|
27
|
-
|
28
|
-
# Demonstrate path retrieval
|
29
|
-
if folders.any?
|
30
|
-
first_folder = folders.first
|
31
|
-
path = client.get_folder_path(first_folder.uid)
|
32
|
-
puts "Path to '#{first_folder.name}': #{path}"
|
33
|
-
|
34
|
-
# Show ancestors
|
35
|
-
ancestors = fm.get_ancestors(first_folder.uid)
|
36
|
-
if ancestors.any?
|
37
|
-
puts "Ancestors of '#{first_folder.name}':"
|
38
|
-
ancestors.each { |a| puts " - #{a.name}" }
|
39
|
-
else
|
40
|
-
puts "No ancestors (root folder or orphan)"
|
41
|
-
end
|
42
|
-
|
43
|
-
# Show descendants
|
44
|
-
descendants = fm.get_descendants(first_folder.uid)
|
45
|
-
if descendants.any?
|
46
|
-
puts "Descendants of '#{first_folder.name}':"
|
47
|
-
descendants.each { |d| puts " - #{d.name}" }
|
48
|
-
else
|
49
|
-
puts "No descendants (leaf folder)"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
puts ""
|
54
|
-
|
55
|
-
# Find folder by name
|
56
|
-
puts "Searching for folders:"
|
57
|
-
puts "-" * 40
|
58
|
-
|
59
|
-
# Example: Find a folder named "Personal"
|
60
|
-
personal = client.find_folder_by_name("Personal")
|
61
|
-
if personal
|
62
|
-
puts "Found folder 'Personal' with UID: #{personal.uid}"
|
63
|
-
puts "Path: #{client.get_folder_path(personal.uid)}"
|
64
|
-
else
|
65
|
-
puts "No folder named 'Personal' found"
|
66
|
-
end
|
67
|
-
|
68
|
-
# Get all records organized by folder
|
69
|
-
puts ""
|
70
|
-
puts "Records by Folder:"
|
71
|
-
puts "-" * 40
|
72
|
-
|
73
|
-
response = client.get_secrets
|
74
|
-
|
75
|
-
# Group records by folder
|
76
|
-
records_by_folder = {}
|
77
|
-
root_records = []
|
78
|
-
|
79
|
-
response.records.each do |record|
|
80
|
-
if record.folder_uid && !record.folder_uid.empty?
|
81
|
-
records_by_folder[record.folder_uid] ||= []
|
82
|
-
records_by_folder[record.folder_uid] << record
|
83
|
-
else
|
84
|
-
root_records << record
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
# Display root records
|
89
|
-
if root_records.any?
|
90
|
-
puts "Root Records (no folder):"
|
91
|
-
root_records.each { |r| puts " - #{r.title}" }
|
92
|
-
puts ""
|
93
|
-
end
|
94
|
-
|
95
|
-
# Display records in each folder
|
96
|
-
folders.each do |folder|
|
97
|
-
folder_records = records_by_folder[folder.uid]
|
98
|
-
if folder_records && folder_records.any?
|
99
|
-
path = client.get_folder_path(folder.uid)
|
100
|
-
puts "Records in '#{path}':"
|
101
|
-
folder_records.each { |r| puts " - #{r.title}" }
|
102
|
-
puts ""
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
rescue => e
|
107
|
-
puts "Error: #{e.message}"
|
108
|
-
puts e.backtrace.first(5).join("\n")
|
109
|
-
end
|
data/examples/full_demo.rb
DELETED
@@ -1,176 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'keeper_secrets_manager'
|
4
|
-
require 'base64'
|
5
|
-
require 'json'
|
6
|
-
|
7
|
-
puts "=== Keeper Secrets Manager Ruby SDK Full Demo ==="
|
8
|
-
puts "This demonstrates all working features of the Ruby SDK"
|
9
|
-
puts "=" * 60
|
10
|
-
|
11
|
-
# Load configuration
|
12
|
-
config_file = File.join(File.dirname(__FILE__), '..', 'config.base64')
|
13
|
-
unless File.exist?(config_file)
|
14
|
-
puts "ERROR: config.base64 not found"
|
15
|
-
exit 1
|
16
|
-
end
|
17
|
-
|
18
|
-
config_base64 = File.read(config_file).strip
|
19
|
-
config_json = Base64.decode64(config_base64)
|
20
|
-
config_data = JSON.parse(config_json)
|
21
|
-
|
22
|
-
# Initialize SDK
|
23
|
-
storage = KeeperSecretsManager::Storage::InMemoryStorage.new(config_data)
|
24
|
-
secrets_manager = KeeperSecretsManager.new(config: storage)
|
25
|
-
|
26
|
-
# 1. Authentication Test
|
27
|
-
puts "\n1. AUTHENTICATION"
|
28
|
-
puts " ✅ Successfully authenticated with Keeper servers"
|
29
|
-
puts " ✅ Using ECDSA signatures"
|
30
|
-
puts " ✅ AES-GCM encryption active"
|
31
|
-
|
32
|
-
# 2. List existing records
|
33
|
-
puts "\n2. READING RECORDS"
|
34
|
-
records = secrets_manager.get_secrets
|
35
|
-
puts " Found #{records.length} Secrets Manager compatible record(s)"
|
36
|
-
|
37
|
-
if records.any?
|
38
|
-
records.each_with_index do |record, i|
|
39
|
-
puts "\n Record ##{i + 1}:"
|
40
|
-
puts " - Title: #{record.title}"
|
41
|
-
puts " - UID: #{record.uid}"
|
42
|
-
puts " - Type: #{record.type}"
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# 3. List folders
|
47
|
-
puts "\n3. LISTING FOLDERS"
|
48
|
-
folders = secrets_manager.get_folders
|
49
|
-
puts " Found #{folders.length} folder(s)"
|
50
|
-
|
51
|
-
if folders.any?
|
52
|
-
puts "\n Available folders:"
|
53
|
-
folders.first(5).each do |folder|
|
54
|
-
puts " - #{folder.name} (#{folder.uid})"
|
55
|
-
end
|
56
|
-
puts " ... and #{folders.length - 5} more" if folders.length > 5
|
57
|
-
end
|
58
|
-
|
59
|
-
# 4. Create a test record
|
60
|
-
puts "\n4. CREATING RECORDS"
|
61
|
-
target_folder = folders.find { |f| f.uid == 'khq76ez6vkTRj3MqUiEGRg' }
|
62
|
-
|
63
|
-
if target_folder
|
64
|
-
puts " Using folder: #{target_folder.name}"
|
65
|
-
|
66
|
-
test_record = {
|
67
|
-
'type' => 'login',
|
68
|
-
'title' => "Ruby SDK Demo #{Time.now.strftime('%Y-%m-%d %H:%M')}",
|
69
|
-
'fields' => [
|
70
|
-
{ 'type' => 'login', 'value' => ['demo@example.com'] },
|
71
|
-
{ 'type' => 'password', 'value' => ['DemoPass123!'] },
|
72
|
-
{ 'type' => 'url', 'value' => ['https://example.com'] },
|
73
|
-
{ 'type' => 'text', 'label' => 'Custom Field', 'value' => ['Custom Value'] }
|
74
|
-
],
|
75
|
-
'notes' => "Created by Ruby SDK demo at #{Time.now}",
|
76
|
-
'custom' => [
|
77
|
-
{ 'type' => 'text', 'label' => 'Environment', 'value' => ['Production'] },
|
78
|
-
{ 'type' => 'text', 'label' => 'API Key', 'value' => ['demo-api-key-123'] }
|
79
|
-
]
|
80
|
-
}
|
81
|
-
|
82
|
-
options = KeeperSecretsManager::Dto::CreateOptions.new
|
83
|
-
options.folder_uid = target_folder.uid
|
84
|
-
|
85
|
-
begin
|
86
|
-
record_uid = secrets_manager.create_secret(test_record, options)
|
87
|
-
puts " ✅ Successfully created record: #{record_uid}"
|
88
|
-
|
89
|
-
# 5. Notation examples
|
90
|
-
puts "\n5. KEEPER NOTATION"
|
91
|
-
|
92
|
-
# Try with existing records
|
93
|
-
if records.any?
|
94
|
-
first_record = records.first
|
95
|
-
puts " Testing with existing record: #{first_record.title}"
|
96
|
-
|
97
|
-
title = secrets_manager.get_notation("keeper://#{first_record.uid}/title")
|
98
|
-
puts " ✅ Title via notation: #{title}"
|
99
|
-
|
100
|
-
type = secrets_manager.get_notation("keeper://#{first_record.uid}/type")
|
101
|
-
puts " ✅ Type via notation: #{type}"
|
102
|
-
else
|
103
|
-
puts " ⚠️ No existing records to test notation with"
|
104
|
-
end
|
105
|
-
|
106
|
-
rescue => e
|
107
|
-
puts " ❌ Error creating record: #{e.message}"
|
108
|
-
end
|
109
|
-
else
|
110
|
-
puts " ❌ Target folder not found"
|
111
|
-
end
|
112
|
-
|
113
|
-
# 6. Dynamic field access
|
114
|
-
puts "\n6. DYNAMIC FIELD ACCESS (JavaScript-style)"
|
115
|
-
if records.any?
|
116
|
-
record = records.first
|
117
|
-
puts " Record: #{record.title}"
|
118
|
-
|
119
|
-
# Try common field accessors
|
120
|
-
if record.respond_to?(:login)
|
121
|
-
puts " ✅ record.login = #{record.login}"
|
122
|
-
end
|
123
|
-
|
124
|
-
if record.respond_to?(:password)
|
125
|
-
puts " ✅ record.password = [hidden]"
|
126
|
-
end
|
127
|
-
|
128
|
-
if record.respond_to?(:url)
|
129
|
-
puts " ✅ record.url = #{record.url}"
|
130
|
-
end
|
131
|
-
|
132
|
-
# Custom fields
|
133
|
-
puts " ✅ Dynamic field access via method_missing"
|
134
|
-
else
|
135
|
-
puts " Creating example record to demonstrate..."
|
136
|
-
example = KeeperSecretsManager::Dto::KeeperRecord.new(
|
137
|
-
'title' => 'Example Record',
|
138
|
-
'fields' => [
|
139
|
-
{ 'type' => 'login', 'value' => ['user@example.com'] },
|
140
|
-
{ 'type' => 'password', 'value' => ['secret123'] }
|
141
|
-
]
|
142
|
-
)
|
143
|
-
|
144
|
-
puts " ✅ example.login = #{example.login}"
|
145
|
-
puts " ✅ example.password = [hidden]"
|
146
|
-
|
147
|
-
# Set new field using set_field method
|
148
|
-
example.set_field('api_key', 'new-api-key-456')
|
149
|
-
puts " ✅ example.get_field_value_single('api_key') = new-api-key-456 (dynamically added)"
|
150
|
-
end
|
151
|
-
|
152
|
-
# 7. Storage options
|
153
|
-
puts "\n7. STORAGE OPTIONS"
|
154
|
-
puts " ✅ InMemoryStorage - Currently in use"
|
155
|
-
puts " ✅ FileStorage - Save config to disk"
|
156
|
-
puts " ✅ EnvironmentStorage - Use environment variables"
|
157
|
-
|
158
|
-
# 8. Summary
|
159
|
-
puts "\n" + "=" * 60
|
160
|
-
puts "SUMMARY: Ruby SDK Features"
|
161
|
-
puts "=" * 60
|
162
|
-
puts "✅ Authentication & Encryption working correctly"
|
163
|
-
puts "✅ Read operations fully functional"
|
164
|
-
puts "✅ Folder listing from dedicated endpoint"
|
165
|
-
puts "✅ Record creation working"
|
166
|
-
puts "✅ Dynamic, JavaScript-style DTOs"
|
167
|
-
puts "✅ Keeper notation support"
|
168
|
-
puts "✅ Multiple storage backends"
|
169
|
-
puts "✅ Ruby 2.7+ compatible"
|
170
|
-
|
171
|
-
puts "\n📝 Notes:"
|
172
|
-
puts "- Client version currently using 'mr' prefix (temporary)"
|
173
|
-
puts "- Will change to 'mb' after registration with Keeper"
|
174
|
-
puts "- Some folders may show decryption errors (expected for certain folder types)"
|
175
|
-
|
176
|
-
puts "\n🎉 The Ruby SDK is ready for use!"
|
@@ -1,176 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# First, we need to add the SDK to Ruby's load path
|
4
|
-
sdk_path = '/Users/mustinov/Source/secrets-manager/sdk/ruby/lib'
|
5
|
-
$LOAD_PATH.unshift(sdk_path) unless $LOAD_PATH.include?(sdk_path)
|
6
|
-
|
7
|
-
# Now we can require the SDK
|
8
|
-
require 'keeper_secrets_manager'
|
9
|
-
|
10
|
-
# Your token from Keeper
|
11
|
-
TOKEN = 'US:3cJU7J7Wea97u4BauFmT2Aeeib3jciyYnqAyNryDDvE'
|
12
|
-
|
13
|
-
# Initialize the SDK
|
14
|
-
puts "Initializing Keeper SDK..."
|
15
|
-
sm = KeeperSecretsManager.new(token: TOKEN)
|
16
|
-
|
17
|
-
# Alternative: Use in-memory storage to reuse credentials
|
18
|
-
# storage = KeeperSecretsManager::Storage::InMemoryStorage.new
|
19
|
-
# sm = KeeperSecretsManager.new(token: TOKEN, config: storage)
|
20
|
-
#
|
21
|
-
# After first use, you can connect without token:
|
22
|
-
# sm = KeeperSecretsManager.new(config: storage)
|
23
|
-
|
24
|
-
puts "\n=== Step 1: List All Secrets ==="
|
25
|
-
begin
|
26
|
-
secrets = sm.get_secrets()
|
27
|
-
puts "Found #{secrets.length} secrets:"
|
28
|
-
secrets.each do |secret|
|
29
|
-
puts " - #{secret.title} (UID: #{secret.uid})"
|
30
|
-
end
|
31
|
-
rescue => e
|
32
|
-
puts "Error: #{e.message}"
|
33
|
-
puts "Make sure to replace TOKEN with your actual Keeper token!"
|
34
|
-
exit 1
|
35
|
-
end
|
36
|
-
|
37
|
-
# Save a secret UID for later tests
|
38
|
-
if secrets.any?
|
39
|
-
test_secret_uid = secrets.first.uid
|
40
|
-
puts "\nUsing secret '#{secrets.first.title}' for testing"
|
41
|
-
end
|
42
|
-
|
43
|
-
puts "\n=== Step 2: Get Secret Details ==="
|
44
|
-
if test_secret_uid
|
45
|
-
secret = sm.get_secrets([test_secret_uid]).first
|
46
|
-
puts "Title: #{secret.title}"
|
47
|
-
puts "Type: #{secret.type}"
|
48
|
-
puts "Fields:"
|
49
|
-
secret.fields.each do |field|
|
50
|
-
puts " - #{field['type']}: #{field['value'].first rescue 'N/A'}"
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
puts "\n=== Step 3: Test Notation ==="
|
55
|
-
if test_secret_uid
|
56
|
-
# Test notation to get password
|
57
|
-
notation = "keeper://#{test_secret_uid}/field/password"
|
58
|
-
begin
|
59
|
-
password = sm.get_notation(notation)
|
60
|
-
puts "Password via notation: [HIDDEN]"
|
61
|
-
rescue => e
|
62
|
-
puts "No password field found"
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
puts "\n=== Step 4: List Folders ==="
|
67
|
-
folders = sm.get_folders()
|
68
|
-
puts "Found #{folders.length} folders:"
|
69
|
-
folders.each do |folder|
|
70
|
-
puts " - #{folder.name} (UID: #{folder.uid})"
|
71
|
-
end
|
72
|
-
|
73
|
-
# Save folder UID for record creation
|
74
|
-
if folders.any?
|
75
|
-
test_folder_uid = folders.first.uid
|
76
|
-
puts "\nWill use folder '#{folders.first.name}' for new records"
|
77
|
-
end
|
78
|
-
|
79
|
-
puts "\n=== Step 5: Create New Secret ==="
|
80
|
-
if test_folder_uid
|
81
|
-
new_record = KeeperSecretsManager::Dto::KeeperRecord.new(
|
82
|
-
title: "Test Record - #{Time.now.strftime('%Y-%m-%d %H:%M')}",
|
83
|
-
type: 'login',
|
84
|
-
fields: [
|
85
|
-
{ 'type' => 'login', 'value' => ['testuser@example.com'] },
|
86
|
-
{ 'type' => 'password', 'value' => ['MySecurePassword123!'] },
|
87
|
-
{ 'type' => 'url', 'value' => ['https://example.com'] }
|
88
|
-
],
|
89
|
-
notes: 'Created by Ruby SDK test'
|
90
|
-
)
|
91
|
-
|
92
|
-
options = KeeperSecretsManager::Dto::CreateOptions.new
|
93
|
-
options.folder_uid = test_folder_uid
|
94
|
-
|
95
|
-
begin
|
96
|
-
new_uid = sm.create_secret(new_record, options)
|
97
|
-
puts "Created new secret with UID: #{new_uid}"
|
98
|
-
created_uid = new_uid
|
99
|
-
rescue => e
|
100
|
-
puts "Could not create record: #{e.message}"
|
101
|
-
end
|
102
|
-
else
|
103
|
-
puts "No folders found - skipping record creation"
|
104
|
-
end
|
105
|
-
|
106
|
-
puts "\n=== Step 6: Update Secret ==="
|
107
|
-
if defined?(created_uid) && created_uid
|
108
|
-
# Fetch the created record
|
109
|
-
record = sm.get_secrets([created_uid]).first
|
110
|
-
|
111
|
-
# Update password
|
112
|
-
record.set_field('password', 'UpdatedPassword456!')
|
113
|
-
record.notes = "Updated at #{Time.now}"
|
114
|
-
|
115
|
-
sm.update_secret(record)
|
116
|
-
puts "Updated secret successfully"
|
117
|
-
end
|
118
|
-
|
119
|
-
puts "\n=== Step 7: File Operations ==="
|
120
|
-
if defined?(created_uid) && created_uid
|
121
|
-
# Create a test file
|
122
|
-
test_content = "This is a test file created at #{Time.now}"
|
123
|
-
File.write('test_upload.txt', test_content)
|
124
|
-
|
125
|
-
# Upload file
|
126
|
-
file_data = File.read('test_upload.txt', mode: 'rb')
|
127
|
-
file_uid = sm.upload_file(created_uid, 'test_upload.txt', file_data)
|
128
|
-
puts "Uploaded file with UID: #{file_uid}"
|
129
|
-
|
130
|
-
# Download file
|
131
|
-
downloaded = sm.download_file(file_uid)
|
132
|
-
puts "Downloaded file: #{downloaded['name']} (#{downloaded['size']} bytes)"
|
133
|
-
|
134
|
-
# Save downloaded file
|
135
|
-
File.write('test_download.txt', downloaded['data'], mode: 'wb')
|
136
|
-
puts "Saved to test_download.txt"
|
137
|
-
|
138
|
-
# Cleanup
|
139
|
-
File.delete('test_upload.txt') if File.exist?('test_upload.txt')
|
140
|
-
File.delete('test_download.txt') if File.exist?('test_download.txt')
|
141
|
-
end
|
142
|
-
|
143
|
-
puts "\n=== Step 8: Test TOTP ==="
|
144
|
-
# Look for a record with TOTP
|
145
|
-
totp_found = false
|
146
|
-
secrets.each do |secret|
|
147
|
-
begin
|
148
|
-
totp_url = sm.get_notation("keeper://#{secret.uid}/field/oneTimeCode")
|
149
|
-
if totp_url && totp_url.start_with?('otpauth://')
|
150
|
-
puts "Found TOTP in '#{secret.title}'"
|
151
|
-
params = KeeperSecretsManager::TOTP.parse_url(totp_url)
|
152
|
-
code = KeeperSecretsManager::TOTP.generate_code(params['secret'])
|
153
|
-
puts "Current TOTP code: #{code}"
|
154
|
-
totp_found = true
|
155
|
-
break
|
156
|
-
end
|
157
|
-
rescue
|
158
|
-
# No TOTP in this record
|
159
|
-
end
|
160
|
-
end
|
161
|
-
puts "No TOTP fields found in any records" unless totp_found
|
162
|
-
|
163
|
-
puts "\n=== Step 9: Cleanup ==="
|
164
|
-
if defined?(created_uid) && created_uid
|
165
|
-
print "Delete test record? (y/n): "
|
166
|
-
response = gets.chomp.downcase
|
167
|
-
if response == 'y'
|
168
|
-
sm.delete_secret([created_uid])
|
169
|
-
puts "Deleted test record"
|
170
|
-
else
|
171
|
-
puts "Kept test record"
|
172
|
-
end
|
173
|
-
end
|
174
|
-
|
175
|
-
puts "\n=== Testing Complete! ==="
|
176
|
-
puts "All basic operations tested successfully"
|
data/examples/simple_test.rb
DELETED
@@ -1,162 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# Add the Ruby SDK to the load path
|
4
|
-
$LOAD_PATH.unshift('/Users/mustinov/Source/secrets-manager/sdk/ruby/lib')
|
5
|
-
|
6
|
-
# Load the SDK
|
7
|
-
require 'keeper_secrets_manager'
|
8
|
-
require 'logger'
|
9
|
-
|
10
|
-
# Enable debug logging
|
11
|
-
logger = Logger.new(STDOUT)
|
12
|
-
logger.level = Logger::DEBUG
|
13
|
-
|
14
|
-
begin
|
15
|
-
# Connect to Keeper
|
16
|
-
puts "Connecting to Keeper..."
|
17
|
-
# IMPORTANT: Replace with your own configuration
|
18
|
-
# You can get a one-time token from: https://keepersecurity.com/secrets-manager
|
19
|
-
#
|
20
|
-
# Option 1: Use one-time token
|
21
|
-
# token = 'US:YOUR_ONE_TIME_TOKEN_HERE'
|
22
|
-
# sm = KeeperSecretsManager.from_token(token)
|
23
|
-
#
|
24
|
-
# Option 2: Use base64 config from environment or file
|
25
|
-
config_base64 = ENV['KSM_CONFIG_BASE64'] || 'YOUR_BASE64_CONFIG_HERE'
|
26
|
-
storage_from_b64 = KeeperSecretsManager::Storage::InMemoryStorage.new(config_base64)
|
27
|
-
sm = KeeperSecretsManager.new(config: storage_from_b64, logger: logger)
|
28
|
-
|
29
|
-
# Alternative: Use in-memory storage to save credentials after first use
|
30
|
-
# storage = KeeperSecretsManager::Storage::InMemoryStorage.new
|
31
|
-
# sm = KeeperSecretsManager.new(token: TOKEN, config: storage)
|
32
|
-
#
|
33
|
-
# For subsequent connections (no token needed):
|
34
|
-
# sm = KeeperSecretsManager.new(config: storage)
|
35
|
-
|
36
|
-
# Get all secrets
|
37
|
-
puts "\nFetching secrets..."
|
38
|
-
response = sm.get_secrets(full_response: true)
|
39
|
-
secrets = response.records
|
40
|
-
|
41
|
-
# Display results
|
42
|
-
puts "\nFound #{secrets.length} secrets:"
|
43
|
-
puts "Found #{response.folders.length} folders:"
|
44
|
-
|
45
|
-
# Show folder structure
|
46
|
-
if response.folders.any?
|
47
|
-
puts "\nFolder structure:"
|
48
|
-
response.folders.each do |folder|
|
49
|
-
puts " - #{folder.name} (#{folder.uid})"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
# Show secrets with their folders and all fields
|
54
|
-
puts "\nShowing first 5 secrets with all fields:"
|
55
|
-
puts "=" * 80
|
56
|
-
secrets.first(500).each_with_index do |secret, i|
|
57
|
-
folder_info = secret.folder_uid && !secret.folder_uid.empty? ? " [in folder: #{secret.folder_uid}]" : " [in root]"
|
58
|
-
puts "\n#{i+1}. #{secret.title}#{folder_info}"
|
59
|
-
puts " Type: #{secret.type}"
|
60
|
-
puts " UID: #{secret.uid}"
|
61
|
-
|
62
|
-
# Show all fields
|
63
|
-
if secret.fields && secret.fields.any?
|
64
|
-
puts " Fields:"
|
65
|
-
secret.fields.each do |field|
|
66
|
-
field_type = field['type'] || 'unknown'
|
67
|
-
field_label = field['label'] || field_type
|
68
|
-
|
69
|
-
# Get the value(s)
|
70
|
-
values = field['value'] || []
|
71
|
-
values = [values] unless values.is_a?(Array)
|
72
|
-
|
73
|
-
# Format based on field type
|
74
|
-
case field_type
|
75
|
-
when 'password', 'secret', 'pinCode', 'securityQuestion'
|
76
|
-
# Hide sensitive data
|
77
|
-
display_values = values.map { |v| '********' }
|
78
|
-
when 'privateKey'
|
79
|
-
# Show first/last few characters of private keys
|
80
|
-
display_values = values.map do |v|
|
81
|
-
if v && v.length > 20
|
82
|
-
"#{v[0..10]}...#{v[-10..-1]}"
|
83
|
-
else
|
84
|
-
v
|
85
|
-
end
|
86
|
-
end
|
87
|
-
when 'fileRef'
|
88
|
-
# Show file reference info
|
89
|
-
display_values = values.map { |v| "File: #{v}" }
|
90
|
-
else
|
91
|
-
# Show full value for non-sensitive fields
|
92
|
-
display_values = values
|
93
|
-
end
|
94
|
-
|
95
|
-
# Print field info
|
96
|
-
if display_values.length == 1
|
97
|
-
puts " - #{field_label} (#{field_type}): #{display_values.first}"
|
98
|
-
elsif display_values.length > 1
|
99
|
-
puts " - #{field_label} (#{field_type}):"
|
100
|
-
display_values.each { |v| puts " • #{v}" }
|
101
|
-
else
|
102
|
-
puts " - #{field_label} (#{field_type}): [empty]"
|
103
|
-
end
|
104
|
-
end
|
105
|
-
else
|
106
|
-
puts " No fields"
|
107
|
-
end
|
108
|
-
|
109
|
-
# Show custom fields if any
|
110
|
-
if secret.custom && secret.custom.any?
|
111
|
-
puts " Custom fields:"
|
112
|
-
secret.custom.each do |custom_field|
|
113
|
-
puts " - #{custom_field['label']}: #{custom_field['value']}"
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# Show files if any
|
118
|
-
if secret.files && secret.files.any?
|
119
|
-
puts " Files:"
|
120
|
-
secret.files.each do |file|
|
121
|
-
file_name = file['title'] || file['name'] || 'unnamed'
|
122
|
-
file_size = file['size'] || 0
|
123
|
-
file_uid = file['fileUid'] || file['uid']
|
124
|
-
puts " - #{file_name} (#{file_size} bytes) [UID: #{file_uid}]"
|
125
|
-
|
126
|
-
# Download the file if it's small (under 1MB for demo)
|
127
|
-
if file_size < 1_000_000 && file_uid
|
128
|
-
begin
|
129
|
-
puts " Downloading..."
|
130
|
-
downloaded_file = sm.download_file(file)
|
131
|
-
|
132
|
-
# Save to downloads folder
|
133
|
-
download_dir = "downloads"
|
134
|
-
Dir.mkdir(download_dir) unless Dir.exist?(download_dir)
|
135
|
-
|
136
|
-
safe_filename = File.basename(downloaded_file['name'] || file_name)
|
137
|
-
file_path = File.join(download_dir, safe_filename)
|
138
|
-
File.binwrite(file_path, downloaded_file['data'])
|
139
|
-
|
140
|
-
puts " ✅ Saved to: #{file_path}"
|
141
|
-
rescue => e
|
142
|
-
puts " ❌ Download failed: #{e.message}"
|
143
|
-
end
|
144
|
-
elsif file_size >= 1_000_000
|
145
|
-
puts " ⚠️ File too large for demo (>1MB)"
|
146
|
-
end
|
147
|
-
end
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
puts "\n" + "=" * 80
|
152
|
-
puts "\nTotal: #{secrets.length} secrets (showing first #{[secrets.length, 500].min})"
|
153
|
-
puts "\nSuccess! The Ruby SDK is working correctly."
|
154
|
-
|
155
|
-
rescue => e
|
156
|
-
puts "\nError: #{e.message}"
|
157
|
-
puts "\nTroubleshooting:"
|
158
|
-
puts "1. Make sure you replaced TOKEN with your actual Keeper token"
|
159
|
-
puts "2. Token format should be: US:xxxxxxxxxxxxx (or EU:xxxxx for Europe)"
|
160
|
-
puts "3. Tokens are one-time use - get a new one if this fails"
|
161
|
-
puts "4. Check that the SDK path is correct: /Users/mustinov/Source/secrets-manager/sdk/ruby/lib"
|
162
|
-
end
|