Merge pull request #183 from ukd1/auth-v1

Attempt at Auth :)
This commit is contained in:
siddontang 2015-09-13 10:53:23 +08:00
commit f0dab4218f
8 changed files with 128 additions and 9 deletions

View File

@ -22,6 +22,7 @@ LedisDB now supports multiple different databases as backends.
+ Replication to guarantee data safety.
+ Supplies tools to load, dump, and repair database.
+ Supports cluster, use [xcodis](https://github.com/siddontang/xcodis)
+ Authentication (though, not via http)
## Build and Install

View File

@ -91,6 +91,8 @@ type SnapshotConfig struct {
type Config struct {
m sync.RWMutex `toml:"-"`
AuthPassword string `toml:"auth_password"`
FileName string `toml:"-"`
Addr string `toml:"addr"`
@ -168,6 +170,9 @@ func NewConfigDefault() *Config {
cfg.SlaveOf = ""
cfg.Readonly = false
// Disable Auth by default, by setting password to blank
cfg.AuthPassword = ""
// default databases number
cfg.Databases = 16

View File

@ -25,6 +25,9 @@ slaveof = ""
# for readonly mode, only replication and flushall can write
readonly = false
# Authentication (for non-http connections). Connect, then use the AUTH command to authenticate.
# auth_password = "russellwashere"
# Choose which backend storage to use, now support:
#
# leveldb

View File

@ -10,9 +10,49 @@ import (
)
var testAppOnce sync.Once
var testAppAuthOnce sync.Once
var testApp *App
var testLedisClient *goredis.Client
var testLedisClientAuth *goredis.Client
func getTestConnAuth(password string) *goredis.PoolConn {
startTestAppAuth(password)
conn, _ := testLedisClientAuth.Get()
return conn
}
func newTestLedisClientAuth() {
testLedisClientAuth = goredis.NewClient("127.0.0.1:20000", "")
testLedisClientAuth.SetMaxIdleConns(4)
}
func startTestAppAuth(password string) {
f := func() {
newTestLedisClientAuth()
cfg := config.NewConfigDefault()
cfg.DataDir = "/tmp/testdb_auth"
os.RemoveAll(cfg.DataDir)
cfg.Addr = "127.0.0.1:20000"
cfg.HttpAddr = "127.0.0.1:20001"
cfg.AuthPassword = password
os.RemoveAll(cfg.DataDir)
var err error
testApp, err = NewApp(cfg)
if err != nil {
println(err.Error())
panic(err)
}
go testApp.Run()
}
testAppAuthOnce.Do(f)
}
func newTestLedisClient() {
testLedisClient = goredis.NewClient("127.0.0.1:16380", "")
@ -36,7 +76,7 @@ func startTestApp() {
cfg.Addr = "127.0.0.1:16380"
cfg.HttpAddr = "127.0.0.1:21181"
os.RemoveAll("/tmp/testdb")
os.RemoveAll(cfg.DataDir)
var err error
testApp, err = NewApp(cfg)

View File

@ -64,6 +64,8 @@ type client struct {
cmd string
args [][]byte
isAuthed bool
resp responseWriter
syncBuf bytes.Buffer
@ -86,6 +88,7 @@ func newClient(app *App) *client {
c.app = app
c.ldb = app.ldb
c.isAuthed = false || c.authEnabled()
c.db, _ = app.ldb.Select(0) //use default db
return c
@ -95,6 +98,10 @@ func (c *client) close() {
}
func (c *client) authEnabled() bool {
return len(c.app.cfg.AuthPassword) > 0
}
func (c *client) perform() {
var err error
@ -104,6 +111,8 @@ func (c *client) perform() {
err = ErrEmptyCommand
} else if exeCmd, ok := regCmds[c.cmd]; !ok {
err = ErrNotFound
} else if c.authEnabled() && !c.isAuthed && c.cmd != "auth" {
err = ErrNotAuthenticated
} else {
// if c.db.IsTransaction() {
// if _, ok := txUnsupportedCmds[c.cmd]; ok {
@ -120,7 +129,6 @@ func (c *client) perform() {
// }
err = exeCmd(c)
}
if c.app.access != nil {

View File

@ -14,6 +14,21 @@ func pingCommand(c *client) error {
return nil
}
func authCommand(c *client) error {
if len(c.args) != 1 {
return ErrCmdParams
}
if c.app.cfg.AuthPassword == string(c.args[0]) {
c.isAuthed = true
c.resp.writeStatus(OK)
return nil
} else {
c.isAuthed = false
return ErrAuthenticationFailure
}
}
func echoCommand(c *client) error {
if len(c.args) != 1 {
return ErrCmdParams
@ -156,6 +171,7 @@ func configCommand(c *client) error {
}
func init() {
register("auth", authCommand)
register("ping", pingCommand)
register("echo", echoCommand)
register("select", selectCommand)

View File

@ -6,6 +6,50 @@ import (
"github.com/siddontang/goredis"
)
func TestAuth(t *testing.T) {
c1 := getTestConn()
defer c1.Close()
// Should error, no params
_, err := c1.Do("AUTH")
if err == nil {
t.Fatal(err)
}
// Should error, invalid pass
_, err = c1.Do("AUTH", "password")
if err.Error() != " authentication failure" {
t.Fatal("Expected authentication error:", err)
}
c2 := getTestConnAuth("password")
defer c2.Close()
// Login
_, err = c2.Do("AUTH", "password")
if err != nil {
t.Fatal(err)
}
// Should be ok doing a command
_, err = c2.Do("GET", "tmp_select_key")
if err != nil {
t.Fatal(err)
}
// Log out by sending wrong pass
_, err = c2.Do("AUTH", "wrong password")
if err.Error() != " authentication failure" {
t.Fatal("Expected authentication error:", err)
}
// Should fail doing a command as we're logged out
_, err = c2.Do("GET", "tmp_select_key")
if err.Error() != " not authenticated" {
t.Fatal("Expected authentication error:", err)
}
}
func TestXSelect(t *testing.T) {
c1 := getTestConn()
defer c1.Close()

View File

@ -9,6 +9,8 @@ import (
var (
ErrEmptyCommand = errors.New("empty command")
ErrNotFound = errors.New("command not found")
ErrNotAuthenticated = errors.New("not authenticated")
ErrAuthenticationFailure = errors.New("authentication failure")
ErrCmdParams = errors.New("invalid command param")
ErrValue = errors.New("value is not an integer or out of range")
ErrSyntax = errors.New("syntax error")