Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

### Fixes

* fix(ruby): IAM authentication now requires TLS. Configuring IAM auth without TLS is rejected at client creation instead of sending the IAM token over a plaintext connection ([#397](https://github.com/valkey-io/valkey-glide-ruby/pull/397))

* fix(ruby): reject `cluster_mode: true` without an explicit seed instead of silently connecting to the standalone `127.0.0.1:6379` default ([#344](https://github.com/valkey-io/valkey-glide-ruby/pull/344))

* fix(ruby): treat an empty `lib_name` or `client_info_tag` as "not configured". Previously `lib_name: ""` plus a tag composed `(tag)` instead of `GlideRuby(tag)`, which glide-core rejects, surfacing as `Valkey::CannotConnectError` at client creation ([#246](https://github.com/valkey-io/valkey-glide-ruby/pull/246))
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ client.get("foo")
| `url` | `redis://`, `rediss://`, `valkey://`, or `valkeys://` URI (merged with explicit options) |
| `db` | Database index (standalone only) |
| `password`, `username` | Authentication |
| `iam_config` | A `Valkey::IamAuthConfig` for AWS IAM authentication. Requires `username` and cannot be combined with `password`. |
| `iam_config` | A `Valkey::IamAuthConfig` for AWS IAM authentication. Requires `username` and TLS (`ssl: true`), and cannot be combined with `password`. |
| `timeout` | Request timeout in seconds (default `5.0`) |
| `connect_timeout` | Connection timeout in seconds |
| `ssl`| Enable TLS if true |
Expand Down
44 changes: 39 additions & 5 deletions test/integration/valkey/iam_authentication_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,11 @@ module ValkeyTests
# server without IAM enforcement, using the placeholder credentials installed by
# Helper::MockAwsCredentials.
#
# Note: IAM requires TLS which is not set up for cluster tests.
#
# What they confirm:
# - `iam_config:` and `username:` reach GLIDE Core, which resolves credentials,
# signs a token, and authenticates with it in standalone and cluster mode.
# signs a token, and authenticates with it over a TLS connection.
# - `refresh_iam_token` round-trips through FFI for every ServiceType.
# - Ruby rejects IAM configuration without a username, Core's IAM interval
# validation errors surface as Valkey::CannotConnectError, and refreshing
Expand All @@ -18,8 +20,10 @@ module ValkeyTests
# signature, cluster name, region, service, and expiry are never checked.
# - Real AWS credential resolution (profiles, instance metadata, STS).
# - Automatic refresh at the configured interval.
# - Behavior against real ElastiCache or MemoryDB, including required TLS.
# - Behavior against real ElastiCache or MemoryDB.
module IamAuthentication
include SslHelper

IAM_USERNAME = "default"
IAM_CLUSTER_NAME = "test-cluster"
IAM_REGION = "us-east-1"
Expand All @@ -30,7 +34,9 @@ module IamAuthentication
:test_iam_client_creation_and_manual_refresh,
service: [Valkey::ServiceType::ELASTICACHE, Valkey::ServiceType::MEMORYDB]
) do |service|
client = _new_client(username: IAM_USERNAME, iam_config: iam_config(service: service))
skip_unless_tls_available

client = _new_iam_tls_client(username: IAM_USERNAME, iam_config: iam_config(service: service))

assert_equal "PONG", client.ping
assert_equal "OK", client.refresh_iam_token
Expand All @@ -45,8 +51,14 @@ def test_iam_requires_username
assert_includes error.message, "username is required for iam_config"
end

# The refresh-interval checks live in GLIDE Core, behind the IAM-requires-TLS
# guard, so these must connect over TLS to reach the interval validation
# instead of being rejected for plaintext first.
Comment thread
Aryex marked this conversation as resolved.
def test_invalid_iam_refresh_interval_fails_creation
skip_unless_tls_available

error = assert_iam_creation_fails(
tls: true,
username: IAM_USERNAME,
iam_config: iam_config(refresh_interval_seconds: 0)
)
Expand All @@ -55,7 +67,10 @@ def test_invalid_iam_refresh_interval_fails_creation
end

def test_max_iam_refresh_interval_fails_creation
skip_unless_tls_available

error = assert_iam_creation_fails(
tls: true,
username: IAM_USERNAME,
iam_config: iam_config(refresh_interval_seconds: 43_200)
)
Expand All @@ -80,6 +95,23 @@ def test_refresh_iam_token_without_iam

private

def _new_iam_tls_client(**options)
Valkey.new(
host: "127.0.0.1",
port: defined?(SSL_PORT) ? SSL_PORT : 6380,
ssl: true,
ssl_params: { ca_file: ssl_ca_cert_path },
timeout: TIMEOUT,
**options
)
end

def skip_unless_tls_available
skip("TLS tests disabled via SKIP_TLS_TESTS") if defined?(SKIP_TLS_TESTS) && SKIP_TLS_TESTS
skip("no TLS server for the cluster context") if cluster_mode?
skip("TLS_CERT_DIR is not set") if ENV.fetch("TLS_CERT_DIR", "").empty?
end

def iam_config(service: Valkey::ServiceType::ELASTICACHE, refresh_interval_seconds: nil)
Valkey::IamAuthConfig.new(
cluster_name: IAM_CLUSTER_NAME,
Expand All @@ -89,9 +121,11 @@ def iam_config(service: Valkey::ServiceType::ELASTICACHE, refresh_interval_secon
)
end

def assert_iam_creation_fails(**options)
def assert_iam_creation_fails(tls: false, **options)
client = nil
assert_raises(Valkey::CannotConnectError) { client = _new_client(**options) }
assert_raises(Valkey::CannotConnectError) do
client = tls ? _new_iam_tls_client(**options) : _new_client(**options)
end
ensure
client&.close
end
Expand Down
2 changes: 1 addition & 1 deletion valkey-glide
Submodule valkey-glide updated 182 files
Loading