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
Username string
Password string
MaxRetries int
@ -130,6 +131,7 @@ func (opt *ClusterOptions) clientOptions() *Options {
MaxRetries: opt.MaxRetries,
MinRetryBackoff: opt.MinRetryBackoff,
MaxRetryBackoff: opt.MaxRetryBackoff,
Username: opt.Username,
Password: opt.Password,
readOnly: opt.ReadOnly,

View File

@ -302,6 +302,7 @@ type Cmdable interface {
type StatefulCmdable interface {
Cmdable
Auth(password string) *StatusCmd
AuthACL(username, password string) *StatusCmd
Select(index int) *StatusCmd
SwapDB(index1, index2 int) *StatusCmd
ClientSetName(name string) *BoolCmd
@ -324,6 +325,15 @@ func (c statefulCmdable) Auth(password string) *StatusCmd {
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 {
cmd := NewStringCmd("echo", message)
_ = c(cmd)

View File

@ -40,8 +40,13 @@ type Options struct {
// Hook that is called when new connection is established.
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
// 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
// Database to be selected after connecting to the server.
DB int
@ -187,6 +192,7 @@ func ParseURL(redisURL string) (*Options, error) {
}
if u.User != nil {
o.Username = u.User.Username()
if p, ok := u.User.Password(); ok {
o.Password = p
}

View File

@ -15,56 +15,86 @@ func TestParseURL(t *testing.T) {
db int
tls bool
err error
user string
pass string
}{
{
"redis://localhost:123/1",
"localhost:123",
1, false, nil,
"", "",
},
{
"redis://localhost:123",
"localhost:123",
0, false, nil,
"", "",
},
{
"redis://localhost/1",
"localhost:6379",
1, false, nil,
"", "",
},
{
"redis://12345",
"12345:6379",
0, false, nil,
"", "",
},
{
"rediss://localhost:123",
"localhost:123",
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",
"",
0, false, errors.New("no options supported"),
"", "",
},
{
"http://google.com",
"",
0, false, errors.New("invalid redis URL scheme: http"),
"", "",
},
{
"redis://localhost/1/2/3/4",
"",
0, false, errors.New("invalid redis URL path: /1/2/3/4"),
"", "",
},
{
"12345",
"",
0, false, errors.New("invalid redis URL scheme: "),
"", "",
},
{
"redis://localhost/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 {
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 {
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 {

View File

@ -22,6 +22,7 @@ type FailoverOptions struct {
MasterName string
// A seed list of host:port addresses of sentinel nodes.
SentinelAddrs []string
SentinelUsername string
SentinelPassword string
// 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)
OnConnect func(*Conn) error
Username string
Password string
DB int
@ -57,6 +59,7 @@ func (opt *FailoverOptions) options() *Options {
OnConnect: opt.OnConnect,
DB: opt.DB,
Username: opt.Username,
Password: opt.Password,
MaxRetries: opt.MaxRetries,
@ -88,6 +91,7 @@ func NewFailoverClient(failoverOpt *FailoverOptions) *Client {
failover := &sentinelFailover{
masterName: failoverOpt.MasterName,
sentinelAddrs: failoverOpt.SentinelAddrs,
username: failoverOpt.SentinelUsername,
password: failoverOpt.SentinelPassword,
opt: opt,
@ -281,6 +285,7 @@ type sentinelFailover struct {
sentinelAddrs []string
opt *Options
username string
password string
pool *pool.ConnPool
@ -372,6 +377,7 @@ func (c *sentinelFailover) masterAddr() (string, error) {
Addr: sentinelAddr,
Dialer: c.opt.Dialer,
Username: c.username,
Password: c.password,
MaxRetries: c.opt.MaxRetries,

View File

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