From 623794feb2a5d3f7a742a44e2fd40f5a4f1bd61a Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Sat, 12 Sep 2015 17:47:10 -0700 Subject: [PATCH 1/3] Attempt at Auth :) --- config/config.go | 7 ++++++ server/app_test.go | 46 ++++++++++++++++++++++++++++++++++++++- server/client.go | 6 ++++- server/cmd_server.go | 16 ++++++++++++++ server/cmd_server_test.go | 44 +++++++++++++++++++++++++++++++++++++ server/const.go | 16 ++++++++------ 6 files changed, 126 insertions(+), 9 deletions(-) diff --git a/config/config.go b/config/config.go index 390b96c..5d1c539 100644 --- a/config/config.go +++ b/config/config.go @@ -91,6 +91,9 @@ type SnapshotConfig struct { type Config struct { m sync.RWMutex `toml:"-"` + AuthEnabled bool `toml:"auth_enabled"` + AuthPassword string `toml:"auth_password"` + FileName string `toml:"-"` Addr string `toml:"addr"` @@ -168,6 +171,10 @@ func NewConfigDefault() *Config { cfg.SlaveOf = "" cfg.Readonly = false + // Disable Auth by default + cfg.AuthEnabled = false + cfg.AuthPassword = "" + // default databases number cfg.Databases = 16 diff --git a/server/app_test.go b/server/app_test.go index f182e8e..4dbdaa1 100644 --- a/server/app_test.go +++ b/server/app_test.go @@ -10,9 +10,53 @@ 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" + + if password != "" { + cfg.AuthPassword = password + cfg.AuthEnabled = true + } + + 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 +80,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) diff --git a/server/client.go b/server/client.go index 0156d16..23afe08 100644 --- a/server/client.go +++ b/server/client.go @@ -64,6 +64,8 @@ type client struct { cmd string args [][]byte + is_authed bool + resp responseWriter syncBuf bytes.Buffer @@ -86,6 +88,7 @@ func newClient(app *App) *client { c.app = app c.ldb = app.ldb + c.is_authed = false || !app.cfg.AuthEnabled c.db, _ = app.ldb.Select(0) //use default db return c @@ -104,6 +107,8 @@ func (c *client) perform() { err = ErrEmptyCommand } else if exeCmd, ok := regCmds[c.cmd]; !ok { err = ErrNotFound + } else if c.app.cfg.AuthEnabled && !c.is_authed && c.cmd != "auth" { + err = ErrNotAuthenticated } else { // if c.db.IsTransaction() { // if _, ok := txUnsupportedCmds[c.cmd]; ok { @@ -120,7 +125,6 @@ func (c *client) perform() { // } err = exeCmd(c) - } if c.app.access != nil { diff --git a/server/cmd_server.go b/server/cmd_server.go index 0530e33..04d1e04 100644 --- a/server/cmd_server.go +++ b/server/cmd_server.go @@ -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.is_authed = true + c.resp.writeStatus(OK) + return nil + } else { + c.is_authed = 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) diff --git a/server/cmd_server_test.go b/server/cmd_server_test.go index 325d602..e44d62d 100644 --- a/server/cmd_server_test.go +++ b/server/cmd_server_test.go @@ -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() diff --git a/server/const.go b/server/const.go index 73fc6a0..df56e12 100644 --- a/server/const.go +++ b/server/const.go @@ -7,13 +7,15 @@ import ( ) var ( - ErrEmptyCommand = errors.New("empty command") - ErrNotFound = errors.New("command not found") - ErrCmdParams = errors.New("invalid command param") - ErrValue = errors.New("value is not an integer or out of range") - ErrSyntax = errors.New("syntax error") - ErrOffset = errors.New("offset bit is not an natural number") - ErrBool = errors.New("value is not 0 or 1") + 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") + ErrOffset = errors.New("offset bit is not an natural number") + ErrBool = errors.New("value is not 0 or 1") ) var ( From d48cb8c7739d385e92b25a82c8004fcc1c9fd823 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Sat, 12 Sep 2015 19:35:08 -0700 Subject: [PATCH 2/3] Make the auth stuff simpler - just have a password --- README.md | 1 + config/config.go | 4 +--- etc/ledis.conf | 3 +++ server/app_test.go | 6 +----- server/client.go | 8 ++++++-- 5 files changed, 12 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index e14b74d..137beff 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/config/config.go b/config/config.go index 5d1c539..7170815 100644 --- a/config/config.go +++ b/config/config.go @@ -91,7 +91,6 @@ type SnapshotConfig struct { type Config struct { m sync.RWMutex `toml:"-"` - AuthEnabled bool `toml:"auth_enabled"` AuthPassword string `toml:"auth_password"` FileName string `toml:"-"` @@ -171,8 +170,7 @@ func NewConfigDefault() *Config { cfg.SlaveOf = "" cfg.Readonly = false - // Disable Auth by default - cfg.AuthEnabled = false + // Disable Auth by default, by setting password to blank cfg.AuthPassword = "" // default databases number diff --git a/etc/ledis.conf b/etc/ledis.conf index 6135848..7ebd83c 100644 --- a/etc/ledis.conf +++ b/etc/ledis.conf @@ -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 diff --git a/server/app_test.go b/server/app_test.go index 4dbdaa1..1ea0752 100644 --- a/server/app_test.go +++ b/server/app_test.go @@ -37,11 +37,7 @@ func startTestAppAuth(password string) { cfg.Addr = "127.0.0.1:20000" cfg.HttpAddr = "127.0.0.1:20001" - - if password != "" { - cfg.AuthPassword = password - cfg.AuthEnabled = true - } + cfg.AuthPassword = password os.RemoveAll(cfg.DataDir) diff --git a/server/client.go b/server/client.go index 23afe08..98a9f21 100644 --- a/server/client.go +++ b/server/client.go @@ -88,7 +88,7 @@ func newClient(app *App) *client { c.app = app c.ldb = app.ldb - c.is_authed = false || !app.cfg.AuthEnabled + c.is_authed = false || c.authEnabled() c.db, _ = app.ldb.Select(0) //use default db return c @@ -98,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 @@ -107,7 +111,7 @@ func (c *client) perform() { err = ErrEmptyCommand } else if exeCmd, ok := regCmds[c.cmd]; !ok { err = ErrNotFound - } else if c.app.cfg.AuthEnabled && !c.is_authed && c.cmd != "auth" { + } else if c.authEnabled() && !c.is_authed && c.cmd != "auth" { err = ErrNotAuthenticated } else { // if c.db.IsTransaction() { From 7e4bc199fbf8f603826bf0d4e8c43a6136a9f200 Mon Sep 17 00:00:00 2001 From: Russell Smith Date: Sat, 12 Sep 2015 19:50:56 -0700 Subject: [PATCH 3/3] Use more standard go; camel case --- server/client.go | 6 +++--- server/cmd_server.go | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/server/client.go b/server/client.go index 98a9f21..28aca56 100644 --- a/server/client.go +++ b/server/client.go @@ -64,7 +64,7 @@ type client struct { cmd string args [][]byte - is_authed bool + isAuthed bool resp responseWriter @@ -88,7 +88,7 @@ func newClient(app *App) *client { c.app = app c.ldb = app.ldb - c.is_authed = false || c.authEnabled() + c.isAuthed = false || c.authEnabled() c.db, _ = app.ldb.Select(0) //use default db return c @@ -111,7 +111,7 @@ func (c *client) perform() { err = ErrEmptyCommand } else if exeCmd, ok := regCmds[c.cmd]; !ok { err = ErrNotFound - } else if c.authEnabled() && !c.is_authed && c.cmd != "auth" { + } else if c.authEnabled() && !c.isAuthed && c.cmd != "auth" { err = ErrNotAuthenticated } else { // if c.db.IsTransaction() { diff --git a/server/cmd_server.go b/server/cmd_server.go index 04d1e04..e007545 100644 --- a/server/cmd_server.go +++ b/server/cmd_server.go @@ -20,11 +20,11 @@ func authCommand(c *client) error { } if c.app.cfg.AuthPassword == string(c.args[0]) { - c.is_authed = true + c.isAuthed = true c.resp.writeStatus(OK) return nil } else { - c.is_authed = false + c.isAuthed = false return ErrAuthenticationFailure } }