Skip to content

Commit

Permalink
Allow password to be a callable.
Browse files Browse the repository at this point in the history
Fix: redis/redis-rb#1299

Makes it easy to implement short lived password authentication strategies.
  • Loading branch information
byroot committed Dec 10, 2024
1 parent 9273f58 commit a5b5f88
Show file tree
Hide file tree
Showing 4 changed files with 45 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Unreleased

- Allow `password` to be a callable. Makes it easy to implement short lived password authentication strategies.
- Fix a thread safety issue in `hiredis-client` when using the `pubsub` client concurrently.

# 0.22.2
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ redis.call("GET", "mykey")
- `db`: The database to select after connecting, defaults to `0`.
- `id` ID for the client connection, assigns name to current connection by sending `CLIENT SETNAME`.
- `username` Username to authenticate against server, defaults to `"default"`.
- `password` Password to authenticate against server.
- `password` Password to authenticate against server. Can either be a String or a callable that recieve `username` as argument and return a passowrd as a String.
- `timeout`: The general timeout in seconds, default to `1.0`.
- `connect_timeout`: The connection timeout, takes precedence over the general timeout when connecting to the server.
- `read_timeout`: The read timeout, takes precedence over the general timeout when reading responses from the server.
Expand Down
33 changes: 25 additions & 8 deletions lib/redis_client/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ class Config
DEFAULT_DB = 0

module Common
attr_reader :db, :password, :id, :ssl, :ssl_params, :command_builder, :inherit_socket,
:connect_timeout, :read_timeout, :write_timeout, :driver, :connection_prelude, :protocol,
attr_reader :db, :id, :ssl, :ssl_params, :command_builder, :inherit_socket,
:connect_timeout, :read_timeout, :write_timeout, :driver, :protocol,
:middlewares_stack, :custom, :circuit_breaker

alias_method :ssl?, :ssl
Expand Down Expand Up @@ -70,7 +70,7 @@ def initialize(

reconnect_attempts = Array.new(reconnect_attempts, 0).freeze if reconnect_attempts.is_a?(Integer)
@reconnect_attempts = reconnect_attempts
@connection_prelude = build_connection_prelude
@connection_prelude = (build_connection_prelude unless @password&.respond_to?(:call))

circuit_breaker = CircuitBreaker.new(**circuit_breaker) if circuit_breaker.is_a?(Hash)
if @circuit_breaker = circuit_breaker
Expand All @@ -87,6 +87,22 @@ def initialize(
@middlewares_stack = middlewares_stack
end

def connection_prelude
if @password&.respond_to?(:call)
build_connection_prelude
else
@connection_prelude
end
end

def password
if @password&.respond_to?(:call)
@password.call(username)
else
@password
end
end

def username
@username || DEFAULT_USERNAME
end
Expand Down Expand Up @@ -151,17 +167,18 @@ def server_url

def build_connection_prelude
prelude = []
pass = password
if protocol == 3
prelude << if @password
["HELLO", "3", "AUTH", @username || DEFAULT_USERNAME, @password]
prelude << if pass
["HELLO", "3", "AUTH", username, pass]
else
["HELLO", "3"]
end
elsif @password
elsif pass
prelude << if @username && !@username.empty?
["AUTH", @username, @password]
["AUTH", @username, pass]
else
["AUTH", @password]
["AUTH", pass]
end
end

Expand Down
18 changes: 18 additions & 0 deletions test/redis_client/config_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -218,5 +218,23 @@ def test_custom_field
config = Config.new(custom: { foo: "bar" })
assert_equal({ foo: "bar" }, config.custom)
end

def test_callable_password
called = 0
argument = nil
password_callable = ->(username) do
called += 1
argument = username
username.upcase.reverse
end
config = Config.new(
username: "george",
password: password_callable,
)

assert_equal [%w(HELLO 3 AUTH george EGROEG)], config.connection_prelude
assert_equal 1, called
assert_equal "george", argument
end
end
end

0 comments on commit a5b5f88

Please sign in to comment.