Add AuthACL

This commit is contained in:
filipe oliveira 2020-05-21 08:59:20 +03:00 committed by Vladimir Mihailenco
parent 64bb0b7f3a
commit cf6cf7f450
7 changed files with 70 additions and 2 deletions

View File

@ -57,6 +57,7 @@ type ClusterOptions struct {
OnConnect func(*Conn) error OnConnect func(*Conn) error
Username string
Password string Password string
MaxRetries int MaxRetries int
@ -130,6 +131,7 @@ func (opt *ClusterOptions) clientOptions() *Options {
MaxRetries: opt.MaxRetries, MaxRetries: opt.MaxRetries,
MinRetryBackoff: opt.MinRetryBackoff, MinRetryBackoff: opt.MinRetryBackoff,
MaxRetryBackoff: opt.MaxRetryBackoff, MaxRetryBackoff: opt.MaxRetryBackoff,
Username: opt.Username,
Password: opt.Password, Password: opt.Password,
readOnly: opt.ReadOnly, readOnly: opt.ReadOnly,

View File

@ -302,6 +302,7 @@ type Cmdable interface {
type StatefulCmdable interface { type StatefulCmdable interface {
Cmdable Cmdable
Auth(password string) *StatusCmd Auth(password string) *StatusCmd
AuthACL(username, password string) *StatusCmd
Select(index int) *StatusCmd Select(index int) *StatusCmd
SwapDB(index1, index2 int) *StatusCmd SwapDB(index1, index2 int) *StatusCmd
ClientSetName(name string) *BoolCmd ClientSetName(name string) *BoolCmd
@ -324,6 +325,15 @@ func (c statefulCmdable) Auth(password string) *StatusCmd {
return cmd return cmd
} }
// Perform an AUTH command, using the given user and pass.
// Should be used to authenticate the current connection with one of the connections defined in the ACL list
// when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
func (c statefulCmdable) AuthACL(username, password string) *StatusCmd {
cmd := NewStatusCmd("auth", username, password)
_ = c(cmd)
return cmd
}
func (c cmdable) Echo(message interface{}) *StringCmd { func (c cmdable) Echo(message interface{}) *StringCmd {
cmd := NewStringCmd("echo", message) cmd := NewStringCmd("echo", message)
_ = c(cmd) _ = c(cmd)

View File

@ -40,8 +40,13 @@ type Options struct {
// Hook that is called when new connection is established. // Hook that is called when new connection is established.
OnConnect func(*Conn) error OnConnect func(*Conn) error
// Use the specified Username to authenticate the current connection with one of the connections defined in the ACL
// list when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
Username string
// Optional password. Must match the password specified in the // Optional password. Must match the password specified in the
// requirepass server configuration option. // requirepass server configuration option (if connecting to a Redis 5.0 instance, or lower),
// or the User Password when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system.
Password string Password string
// Database to be selected after connecting to the server. // Database to be selected after connecting to the server.
DB int DB int
@ -187,6 +192,7 @@ func ParseURL(redisURL string) (*Options, error) {
} }
if u.User != nil { if u.User != nil {
o.Username = u.User.Username()
if p, ok := u.User.Password(); ok { if p, ok := u.User.Password(); ok {
o.Password = p o.Password = p
} }

View File

@ -15,56 +15,86 @@ func TestParseURL(t *testing.T) {
db int db int
tls bool tls bool
err error err error
user string
pass string
}{ }{
{ {
"redis://localhost:123/1", "redis://localhost:123/1",
"localhost:123", "localhost:123",
1, false, nil, 1, false, nil,
"", "",
}, },
{ {
"redis://localhost:123", "redis://localhost:123",
"localhost:123", "localhost:123",
0, false, nil, 0, false, nil,
"", "",
}, },
{ {
"redis://localhost/1", "redis://localhost/1",
"localhost:6379", "localhost:6379",
1, false, nil, 1, false, nil,
"", "",
}, },
{ {
"redis://12345", "redis://12345",
"12345:6379", "12345:6379",
0, false, nil, 0, false, nil,
"", "",
}, },
{ {
"rediss://localhost:123", "rediss://localhost:123",
"localhost:123", "localhost:123",
0, true, nil, 0, true, nil,
"", "",
},
{
"redis://:bar@localhost:123",
"localhost:123",
0, false, nil,
"", "bar",
},
{
"redis://foo@localhost:123",
"localhost:123",
0, false, nil,
"foo", "",
},
{
"redis://foo:bar@localhost:123",
"localhost:123",
0, false, nil,
"foo", "bar",
}, },
{ {
"redis://localhost/?abc=123", "redis://localhost/?abc=123",
"", "",
0, false, errors.New("no options supported"), 0, false, errors.New("no options supported"),
"", "",
}, },
{ {
"http://google.com", "http://google.com",
"", "",
0, false, errors.New("invalid redis URL scheme: http"), 0, false, errors.New("invalid redis URL scheme: http"),
"", "",
}, },
{ {
"redis://localhost/1/2/3/4", "redis://localhost/1/2/3/4",
"", "",
0, false, errors.New("invalid redis URL path: /1/2/3/4"), 0, false, errors.New("invalid redis URL path: /1/2/3/4"),
"", "",
}, },
{ {
"12345", "12345",
"", "",
0, false, errors.New("invalid redis URL scheme: "), 0, false, errors.New("invalid redis URL scheme: "),
"", "",
}, },
{ {
"redis://localhost/iamadatabase", "redis://localhost/iamadatabase",
"", "",
0, false, errors.New(`invalid redis database number: "iamadatabase"`), 0, false, errors.New(`invalid redis database number: "iamadatabase"`),
"", "",
}, },
} }
@ -90,6 +120,12 @@ func TestParseURL(t *testing.T) {
if c.tls && o.TLSConfig == nil { if c.tls && o.TLSConfig == nil {
t.Errorf("got nil TLSConfig, expected a TLSConfig") t.Errorf("got nil TLSConfig, expected a TLSConfig")
} }
if o.Username != c.user {
t.Errorf("got %q, expected %q", o.Username, c.user)
}
if o.Password != c.pass {
t.Errorf("got %q, expected %q", o.Password, c.pass)
}
}) })
} }
} }

View File

@ -241,7 +241,11 @@ func (c *baseClient) initConn(ctx context.Context, cn *pool.Conn) error {
_, err := conn.Pipelined(func(pipe Pipeliner) error { _, err := conn.Pipelined(func(pipe Pipeliner) error {
if c.opt.Password != "" { if c.opt.Password != "" {
pipe.Auth(c.opt.Password) if c.opt.Username != "" {
pipe.AuthACL(c.opt.Username, c.opt.Password)
} else {
pipe.Auth(c.opt.Password)
}
} }
if c.opt.DB > 0 { if c.opt.DB > 0 {

View File

@ -22,6 +22,7 @@ type FailoverOptions struct {
MasterName string MasterName string
// A seed list of host:port addresses of sentinel nodes. // A seed list of host:port addresses of sentinel nodes.
SentinelAddrs []string SentinelAddrs []string
SentinelUsername string
SentinelPassword string SentinelPassword string
// Following options are copied from Options struct. // Following options are copied from Options struct.
@ -29,6 +30,7 @@ type FailoverOptions struct {
Dialer func(ctx context.Context, network, addr string) (net.Conn, error) Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
OnConnect func(*Conn) error OnConnect func(*Conn) error
Username string
Password string Password string
DB int DB int
@ -57,6 +59,7 @@ func (opt *FailoverOptions) options() *Options {
OnConnect: opt.OnConnect, OnConnect: opt.OnConnect,
DB: opt.DB, DB: opt.DB,
Username: opt.Username,
Password: opt.Password, Password: opt.Password,
MaxRetries: opt.MaxRetries, MaxRetries: opt.MaxRetries,
@ -88,6 +91,7 @@ func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
failover := &sentinelFailover{ failover := &sentinelFailover{
masterName: failoverOpt.MasterName, masterName: failoverOpt.MasterName,
sentinelAddrs: failoverOpt.SentinelAddrs, sentinelAddrs: failoverOpt.SentinelAddrs,
username: failoverOpt.SentinelUsername,
password: failoverOpt.SentinelPassword, password: failoverOpt.SentinelPassword,
opt: opt, opt: opt,
@ -281,6 +285,7 @@ type sentinelFailover struct {
sentinelAddrs []string sentinelAddrs []string
opt *Options opt *Options
username string
password string password string
pool *pool.ConnPool pool *pool.ConnPool
@ -372,6 +377,7 @@ func (c *sentinelFailover) masterAddr() (string, error) {
Addr: sentinelAddr, Addr: sentinelAddr,
Dialer: c.opt.Dialer, Dialer: c.opt.Dialer,
Username: c.username,
Password: c.password, Password: c.password,
MaxRetries: c.opt.MaxRetries, MaxRetries: c.opt.MaxRetries,

View File

@ -22,6 +22,7 @@ type UniversalOptions struct {
Dialer func(ctx context.Context, network, addr string) (net.Conn, error) Dialer func(ctx context.Context, network, addr string) (net.Conn, error)
OnConnect func(*Conn) error OnConnect func(*Conn) error
Username string
Password string Password string
MaxRetries int MaxRetries int
MinRetryBackoff time.Duration MinRetryBackoff time.Duration
@ -60,6 +61,7 @@ func (o *UniversalOptions) Cluster() *ClusterOptions {
Dialer: o.Dialer, Dialer: o.Dialer,
OnConnect: o.OnConnect, OnConnect: o.OnConnect,
Username: o.Username,
Password: o.Password, Password: o.Password,
MaxRedirects: o.MaxRedirects, MaxRedirects: o.MaxRedirects,
@ -99,6 +101,7 @@ func (o *UniversalOptions) Failover() *FailoverOptions {
OnConnect: o.OnConnect, OnConnect: o.OnConnect,
DB: o.DB, DB: o.DB,
Username: o.Username,
Password: o.Password, Password: o.Password,
MaxRetries: o.MaxRetries, MaxRetries: o.MaxRetries,
@ -133,6 +136,7 @@ func (o *UniversalOptions) Simple() *Options {
OnConnect: o.OnConnect, OnConnect: o.OnConnect,
DB: o.DB, DB: o.DB,
Username: o.Username,
Password: o.Password, Password: o.Password,
MaxRetries: o.MaxRetries, MaxRetries: o.MaxRetries,