mirror of https://github.com/go-redis/redis.git
Add AuthACL
This commit is contained in:
parent
64bb0b7f3a
commit
cf6cf7f450
|
@ -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,
|
||||||
|
|
||||||
|
|
10
commands.go
10
commands.go
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
4
redis.go
4
redis.go
|
@ -241,8 +241,12 @@ 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 != "" {
|
||||||
|
if c.opt.Username != "" {
|
||||||
|
pipe.AuthACL(c.opt.Username, c.opt.Password)
|
||||||
|
} else {
|
||||||
pipe.Auth(c.opt.Password)
|
pipe.Auth(c.opt.Password)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if c.opt.DB > 0 {
|
if c.opt.DB > 0 {
|
||||||
pipe.Select(c.opt.DB)
|
pipe.Select(c.opt.DB)
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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,
|
||||||
|
|
Loading…
Reference in New Issue