Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
7d3e28d
KSM-685: Fix Ruby SDK to send subFolderUid parameter when creating re…
stas-schaller Nov 7, 2025
eaee676
KSM-686: Implement disaster recovery caching with CachingPostFunction…
stas-schaller Nov 7, 2025
a5b9432
Updated version and changelog
stas-schaller Nov 7, 2025
56754eb
Fix Ruby SDK SBOM generation to scan built gem instead of directory w…
stas-schaller Nov 11, 2025
d1b4b81
KSM-685: added missing inner_folder_uid field in DTO for record
stas-schaller Nov 11, 2025
a3804e9
Added pry console script for development tools
stas-schaller Nov 11, 2025
9e1a453
KSM-687: Add missing DTO fields and methods for SDK parity with other…
stas-schaller Nov 11, 2025
7db5ef0
Expand Ruby SDK test coverage with TOTP, file operations, and folder …
stas-schaller Nov 11, 2025
a812b66
Fixed badly anchored regular expression in test
stas-schaller Nov 11, 2025
19c2c7a
Revert "Fix Ruby SDK SBOM generation to scan built gem instead of dir…
stas-schaller Nov 12, 2025
d272da1
Add Ruby SDK to SDKs table in root README
stas-schaller Nov 12, 2025
2e89fde
Fix Ruby gem name in README (use underscores, not hyphens)
stas-schaller Nov 12, 2025
01ca905
Add from_config() convenience method for base64 config initialization
stas-schaller Nov 13, 2025
f1f2292
Fix example files to use correct SDK APIs
stas-schaller Nov 13, 2025
c6b80c5
Update CHANGELOG for v17.1.1 release
stas-schaller Nov 13, 2025
b15b344
Update CHANGELOG.md for Keep a Changelog 1.1.0 compliance
stas-schaller Nov 13, 2025
117933a
Simplify README to match other SDK patterns (docs.keeper.io is source…
stas-schaller Nov 13, 2025
a2c167c
Bump version to 17.2.0 (minor release for new API methods and features)
stas-schaller Nov 14, 2025
4c82fdc
Add PAM linked records example and update all examples with new featu…
stas-schaller Nov 14, 2025
c6c593b
KSM-687: Add complete_transaction method for PAM rotation workflows
stas-schaller Nov 17, 2025
3267c7a
KSM-692: Add HTTP proxy support with environment variable fallback
stas-schaller Nov 17, 2025
9ced3ca
KSM-694: Add file upload and notation convenience methods
stas-schaller Nov 17, 2025
a884602
Add integration tests for thumbnail download workflow
stas-schaller Nov 17, 2025
954f923
Add integration tests for UpdateOptions.links_to_remove
stas-schaller Nov 17, 2025
02e8862
Add unit tests for QueryOptions filtering
stas-schaller Nov 17, 2025
cee027a
Add integration tests for PAM linked records GraphSync
stas-schaller Nov 17, 2025
14258e9
Add disaster recovery caching integration tests
stas-schaller Nov 17, 2025
9fb4b3a
Update CHANGELOG for 17.2.0 release with new features
stas-schaller Nov 17, 2025
cff3a3a
Fix file permissions for Ruby SDK config files
stas-schaller Nov 18, 2025
9e1da4e
Merge pull request #871 from Keeper-Security/KSM-696-ruby-secure-perm…
stas-schaller Nov 20, 2025
575bf9d
Add comprehensive unit tests for errors, field_types, and utils modules
stas-schaller Nov 20, 2025
d4b4051
Add comprehensive unit tests for cache and TOTP modules
stas-schaller Nov 20, 2025
6ba51d9
Add unit tests for core SecretsManager initialization and token proce…
stas-schaller Nov 20, 2025
5eb3899
Fix core_spec token test with valid base64-encoded mock data
stas-schaller Nov 20, 2025
aa7b9a3
Fix mock token key length in core_spec.rb
stas-schaller Nov 20, 2025
041e1c9
Fix AES-GCM encrypted data format in core_spec.rb
stas-schaller Nov 20, 2025
62bff48
Mock bind_one_time_token directly instead of encrypted HTTP responses
stas-schaller Nov 20, 2025
11511c2
Merge master into release branch
stas-schaller Dec 18, 2025
0e7a50d
KSM-734: fix notation lookup with record shortcuts
stas-schaller Dec 18, 2025
a574667
KSM-743: add transmission public key #18 for Gov Cloud Dev support
stas-schaller Jan 8, 2026
d8626de
Merge pull request #898 from Keeper-Security/feature/KSM-743-add-key-…
stas-schaller Jan 8, 2026
6d19208
docs: update changelog and README with KSM-743 (Gov Cloud Dev key #18)
stas-schaller Jan 8, 2026
80aafaf
Merge pull request #887 from Keeper-Security/KSM-734-fix-duplicate-ui…
stas-schaller Jan 9, 2026
b655c2c
docs: add KSM-734 fix to changelog and README
stas-schaller Jan 9, 2026
d774209
chore: bump version to 17.3.0 and update CHANGELOG for PAM features
stas-schaller Jan 13, 2026
174e5ab
chore: remove PAM features and comprehensive tests for v17.3.0 release
stas-schaller Jan 13, 2026
66f389d
merge: integrate v17.2.0 bug fixes into v17.3.0
stas-schaller Jan 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/workflows/test.ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ jobs:
run: |
bundle exec rubocop || true # Don't fail on linting for now

- name: Run unit tests
- name: Run RSpec tests (unit + integration)
run: |
bundle exec rspec spec/

- name: Run integration tests (mock mode)
- name: Run offline mock test
env:
KEEPER_MOCK_MODE: 'true'
run: |
ruby -I lib test/integration/test_offline_mock.rb || true # Don't fail if file doesn't exist yet
ruby -I lib test/integration/test_offline_mock.rb
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ Common use cases for Secrets Manager include:
**Java** | [Docs](https://docs.keeper.io/secrets-manager/secrets-manager/developer-sdk-library/java-sdk) <br /> [Source](https://github.com/Keeper-Security/secrets-manager/tree/master/sdk/java/core) | ![Java](https://github.com/Keeper-Security/secrets-manager/actions/workflows/test.java.yml/badge.svg) | [![Maven Central](https://img.shields.io/maven-central/v/com.keepersecurity.secrets-manager/core?style=for-the-badge&logo=java&logoColor=white)](https://search.maven.org/artifact/com.keepersecurity.secrets-manager/core) |
**.NET** | [Docs](https://docs.keeper.io/secrets-manager/secrets-manager/developer-sdk-library/.net-sdk) <br /> [Source](https://github.com/Keeper-Security/secrets-manager/tree/master/sdk/dotNet) | ![.NET](https://github.com/Keeper-Security/secrets-manager/actions/workflows/test.dotnet.yml/badge.svg) | [![Nuget](https://img.shields.io/nuget/v/Keeper.SecretsManager?style=for-the-badge&logo=nuget&logoColor=white)](https://www.nuget.org/packages/Keeper.SecretsManager) |
**Go** | [Docs](https://docs.keeper.io/secrets-manager/secrets-manager/developer-sdk-library/golang-sdk) <br /> [Source](https://github.com/Keeper-Security/secrets-manager-go) | ![GoLang](https://github.com/keeper-security/secrets-manager-go/actions/workflows/test.go.yml/badge.svg) | [![Go](https://img.shields.io/github/v/tag/Keeper-Security/secrets-manager-go?label=Go&logo=go&logoColor=white&style=for-the-badge)](https://github.com/Keeper-Security/secrets-manager-go) |
**Ruby** | [Docs](https://docs.keeper.io/secrets-manager/secrets-manager/developer-sdk-library/ruby-sdk) <br /> [Source](https://github.com/Keeper-Security/secrets-manager/tree/master/sdk/ruby) | ![Ruby](https://github.com/Keeper-Security/secrets-manager/actions/workflows/test.ruby.yml/badge.svg) | [![RubyGems](https://img.shields.io/gem/v/keeper_secrets_manager?style=for-the-badge&logo=rubygems&logoColor=white)](https://rubygems.org/gems/keeper_secrets_manager) |

More information about Keeper Secrets Manager, SDKs, tools, and integrations can be found in our [official documentation
portal](https://docs.keeper.io/secrets-manager/secrets-manager/overview)
Expand Down
23 changes: 14 additions & 9 deletions examples/ruby/01_quick_start.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,24 @@
puts 'Make sure to set KSM_TOKEN environment variable or replace with your token'
end

# Method 2: Using base64 configuration (for repeated use)
# After first connection, save your config for reuse
# Method 2: Using saved configuration file (recommended for repeated use)
# After first connection with token, config is saved to keeper_config.json
begin
config_base64 = ENV['KSM_CONFIG'] || 'YOUR_BASE64_CONFIG_STRING'
# Initialize from saved configuration file
secrets_manager = KeeperSecretsManager.from_file('keeper_config.json')

# Initialize with saved configuration
secrets_manager = KeeperSecretsManager.from_config(config_base64)
# Get all secrets
secrets = secrets_manager.get_secrets
puts "\nRetrieved #{secrets.length} secrets from saved config"

# Get specific secret by UID
secret = secrets_manager.get_secret_by_uid('RECORD_UID')
puts "\nSecret details:"
puts " Title: #{secret.title}"
puts " Login: #{secret.fields['login']}"
if secrets.any?
secret = secrets.first
puts "\nSecret details:"
puts " Title: #{secret.title}"
puts " Login: #{secret.login}" if secret.login
end
rescue StandardError => e
puts "Error: #{e.message}"
puts 'Make sure keeper_config.json exists (run with token first)'
end
11 changes: 6 additions & 5 deletions examples/ruby/02_authentication.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,17 @@
puts "✗ Error: #{e.message}"
end

# Method 3: Using configuration file
puts "\n3. Using Configuration File:"
# Method 3: Using configuration file (RECOMMENDED)
puts "\n3. Using Configuration File (Recommended):"
begin
# Save configuration to a file
config_file = 'keeper-config.json'
# This is the recommended approach for most applications
config_file = 'keeper_config.json'

# Initialize with file storage
# Initialize from file storage
sm = KeeperSecretsManager.from_file(config_file)

puts "✓ Connected using config file: #{config_file}"
puts " (This is the recommended method after initial token binding)"
rescue StandardError => e
puts "✗ Error: #{e.message}"
end
Expand Down
42 changes: 38 additions & 4 deletions examples/ruby/03_retrieve_secrets.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

require 'keeper_secrets_manager'

# Initialize (use your preferred method)
config = ENV['KSM_CONFIG'] || 'YOUR_BASE64_CONFIG'
secrets_manager = KeeperSecretsManager.from_config(config)
# Initialize from saved configuration file
secrets_manager = KeeperSecretsManager.from_file('keeper_config.json')

puts "=== Retrieving Secrets ==="

Expand Down Expand Up @@ -81,4 +80,39 @@
end
rescue => e
puts " Notation error: #{e.message}"
end
end

# 7. New DTO Fields (v17.2.0)
puts "\n7. New DTO Fields:"
puts " Access new metadata fields on records"

begin
query_options = KeeperSecretsManager::Dto::QueryOptions.new(request_links: true)
records_with_metadata = secrets_manager.get_secrets([], query_options)

records_with_metadata.first(3).each do |record|
puts "\n #{record.title}"
puts " Editable: #{record.is_editable ? 'Yes' : 'No'}"
puts " Folder UID: #{record.inner_folder_uid}" if record.inner_folder_uid
puts " Has links: #{record.links && record.links.any? ? 'Yes' : 'No'}"

if record.files && record.files.any?
file = record.files.first
puts " File metadata:"
puts " Last modified: #{Time.at(file['lastModified'])}" if file['lastModified']
puts " Has thumbnail: #{file['thumbnailUrl'] ? 'Yes' : 'No'}"
end
end
rescue => e
puts " Error: #{e.message}"
end

# Tips
puts "\n=== Tips ==="
puts '- Use get_secrets() without parameters to retrieve all secrets'
puts '- Use get_secrets([uid]) to retrieve specific secrets by UID'
puts '- Use get_secret_by_title() for quick lookups by name'
puts '- Use notation for quick field access'
puts '- Dynamic field access (record.password) is convenient for standard fields'
puts '- Enable request_links: true to retrieve PAM linked credentials'
puts '- Check is_editable before attempting to modify records'
59 changes: 56 additions & 3 deletions examples/ruby/04_create_update_delete.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

require 'keeper_secrets_manager'

# Initialize
config = ENV['KSM_CONFIG'] || 'YOUR_BASE64_CONFIG'
secrets_manager = KeeperSecretsManager.from_config(config)
# Initialize from saved configuration file
secrets_manager = KeeperSecretsManager.from_file('keeper_config.json')

puts '=== CRUD Operations Example ==='

Expand Down Expand Up @@ -73,6 +72,60 @@
puts " New URL: #{updated.url}"
puts " Notes: #{updated.notes}"

# 3.5. Advanced Update - Password Rotation with Transaction Type
puts "\n3.5. Password rotation with transaction type..."
begin
# Get a fresh copy of the record
secret = secrets_manager.get_secrets([record_uid]).first

# Generate new password
new_password = KeeperSecretsManager::Utils.generate_password(length: 32)
secret.password = new_password

# Update with rotation transaction type
update_options = KeeperSecretsManager::Dto::UpdateOptions.new(
transaction_type: 'rotation'
)

secrets_manager.update_secret_with_options(secret, update_options)
puts '✓ Password rotated with transaction tracking'
puts " New password: #{new_password[0..5]}..." # Show first 6 chars only

rescue StandardError => e
puts "✗ Error: #{e.message}"
end

# 3.6. Advanced Update - Remove File Links
puts "\n3.6. Removing file attachments (if any)..."
begin
# Refresh the record
secret = secrets_manager.get_secrets([record_uid]).first

if secret.files && secret.files.any?
# Find files to remove (e.g., files starting with "old_")
file_uids_to_remove = secret.files
.select { |f| f['name'] =~ /^old_/ }
.map { |f| f['fileUid'] }

if file_uids_to_remove.any?
update_options = KeeperSecretsManager::Dto::UpdateOptions.new(
transaction_type: 'general',
links_to_remove: file_uids_to_remove
)

secrets_manager.update_secret_with_options(secret, update_options)
puts "✓ Removed #{file_uids_to_remove.length} file link(s)"
else
puts " (No old files to remove)"
end
else
puts " (No files attached to record)"
end

rescue StandardError => e
puts "✗ Error: #{e.message}"
end

# 4. DELETE - Remove the secret
puts "\n4. Deleting the secret..."
puts 'Press Enter to delete the test record...'
Expand Down
5 changes: 2 additions & 3 deletions examples/ruby/05_field_types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

require 'keeper_secrets_manager'

# Initialize
config = ENV['KSM_CONFIG'] || 'YOUR_BASE64_CONFIG'
secrets_manager = KeeperSecretsManager.from_config(config)
# Initialize from saved configuration file
secrets_manager = KeeperSecretsManager.from_file('keeper_config.json')

puts '=== Field Types Example ==='

Expand Down
131 changes: 84 additions & 47 deletions examples/ruby/06_files.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
require 'keeper_secrets_manager'
require 'tempfile'

# Initialize
config = ENV['KSM_CONFIG'] || 'YOUR_BASE64_CONFIG'
secrets_manager = KeeperSecretsManager.from_config(config)
# Initialize from saved configuration file
secrets_manager = KeeperSecretsManager.from_file('keeper_config.json')

puts '=== File Operations Example ==='

Expand Down Expand Up @@ -38,68 +37,106 @@
filename = downloaded['name'] || 'downloaded_file'
File.write(filename, downloaded['data'])

puts " Downloaded: #{filename} (#{downloaded['size']} bytes)"
puts "[OK] Downloaded: #{filename} (#{downloaded['size']} bytes)"
puts " Type: #{downloaded['type']}"

# Clean up
File.delete(filename) if File.exist?(filename)

rescue StandardError => e
puts " Download failed: #{e.message}"
puts "[FAIL] Download failed: #{e.message}"
end
end

# 3. Upload a file
puts "\n3. Uploading a file..."
# 2.5. Download file thumbnails (new in v17.2.0)
if records_with_files.any?
puts "\n2.5. Downloading file thumbnails..."
record = records_with_files.first

record.files.each do |file|
# Check if thumbnail is available
if file['thumbnailUrl'] || file['thumbnail_url']
puts " Downloading thumbnail for: #{file['name']}"

begin
thumbnail = secrets_manager.download_thumbnail(file)

# Save thumbnail to disk
thumb_filename = "thumb_#{file['name']}"
File.write(thumb_filename, thumbnail['data'])

puts " [OK] Saved: #{thumb_filename} (#{thumbnail['size']} bytes, #{thumbnail['type']})"

# Clean up
File.delete(thumb_filename) if File.exist?(thumb_filename)
rescue StandardError => e
puts " [FAIL] Thumbnail download failed: #{e.message}"
end
else
puts " No thumbnail available for: #{file['name']}"
end
end
end

# 3. Upload a file (traditional method)
puts "\n3. Uploading a file (traditional method)..."
begin
# Create a test file
test_content = "This is a test file created at #{Time.now}\n"
test_content += "It contains some sample data for demonstration.\n"

# Create or find a record to attach the file to
record = secrets.first || begin
# Create a new record if none exist
# Note: You need to specify a folder_uid where the record will be created
# Get the first available folder
folders = secrets_manager.get_folders
folder_uid = folders.first&.uid
raise 'No folders available. Please create a folder in your vault first.' unless folder_uid

options = KeeperSecretsManager::Dto::CreateOptions.new(folder_uid: folder_uid)
uid = secrets_manager.create_secret({
type: 'login',
title: 'File Upload Test',
fields: [
{ type: 'login', value: ['test@example.com'] },
{ type: 'password', value: ['test123'] }
]
}, options)
secrets_manager.get_secret_by_uid(uid)
# Get a record to attach the file to
record = secrets.first
if record
puts "Uploading to record: #{record.title}"

# Upload the file (traditional method with file data)
file_uid = secrets_manager.upload_file(
record.uid,
test_content,
'test_document.txt',
'Test Document'
)

puts "[OK] Uploaded file with UID: #{file_uid}"
else
puts '[WARN] No records available for file upload test'
end
rescue StandardError => e
puts "[FAIL] Upload failed: #{e.message}"
puts ' Note: File upload requires write permissions'
end

puts "Uploading to record: #{record.title}"

# Upload the file
file_uid = secrets_manager.upload_file(
owner_record_uid: record.uid,
file_name: 'test_document.txt',
file_data: test_content,
mime_type: 'text/plain'
)

puts "✓ Uploaded file with UID: #{file_uid}"

# Verify by downloading
updated_record = secrets_manager.get_secret_by_uid(record.uid)
new_file = updated_record.files.find { |f| f['fileUid'] == file_uid }

if new_file
downloaded = secrets_manager.download_file(new_file)
puts "✓ Verified: #{downloaded['name']}"
# 3.5. Upload file from path (convenience method - NEW in v17.2.0)
puts "\n3.5. Uploading file from disk path (convenience method)..."
begin
# Create a temporary file on disk
temp_file = Tempfile.new(['keeper_test', '.txt'])
temp_file.write("Test file content from disk\nCreated: #{Time.now}")
temp_file.close

record = secrets.first
if record
puts "Uploading from path: #{temp_file.path}"

# Convenience method - reads file automatically
file_uid = secrets_manager.upload_file_from_path(
record.uid,
temp_file.path,
file_title: 'Uploaded from Disk'
)

puts "[OK] Uploaded file with UID: #{file_uid}"
puts " Filename auto-detected: #{File.basename(temp_file.path)}"
else
puts '[WARN] No records available for file upload test'
end

# Clean up
temp_file.unlink
rescue StandardError => e
puts " Upload failed: #{e.message}"
puts ' Note: File upload requires write permissions'
puts "[FAIL] Upload from path failed: #{e.message}"
temp_file&.unlink
end

# 4. Working with different file types
Expand Down
5 changes: 2 additions & 3 deletions examples/ruby/07_folders.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,8 @@

require 'keeper_secrets_manager'

# Initialize
config = ENV['KSM_CONFIG'] || 'YOUR_BASE64_CONFIG'
secrets_manager = KeeperSecretsManager.from_config(config)
# Initialize from saved configuration file
secrets_manager = KeeperSecretsManager.from_file('keeper_config.json')

puts '=== Folder Operations Example ==='

Expand Down
Loading
Loading