From 20abadf41fe8ad8a839d4663364d6da8974238e5 Mon Sep 17 00:00:00 2001 From: Tobie Morgan Hitchcock Date: Thu, 18 Oct 2018 01:22:44 +0100 Subject: [PATCH] Support exporting SQL LOGIN and SQL TOKEN hashes --- db/define.go | 7 ++++++- db/info_test.go | 52 ++++++++++++++++++++++++++----------------------- sql/ast.go | 1 + sql/login.go | 15 +++++++++++--- sql/sql_test.go | 12 ++++++------ sql/string.go | 8 +++++--- sql/tokens.go | 2 ++ 7 files changed, 60 insertions(+), 37 deletions(-) diff --git a/db/define.go b/db/define.go index 001ef7c9..8840e07a 100644 --- a/db/define.go +++ b/db/define.go @@ -50,7 +50,12 @@ func (e *executor) executeDefineLogin(ctx context.Context, ast *sql.DefineLoginS ast.Code = rand.New(128) - ast.Pass, _ = bcrypt.GenerateFromPassword(ast.Pass, bcrypt.DefaultCost) + switch len(ast.Hash) { + default: + ast.Pass = ast.Hash + case 0: + ast.Pass, _ = bcrypt.GenerateFromPassword(ast.Pass, bcrypt.DefaultCost) + } switch ast.Kind { case sql.NAMESPACE: diff --git a/db/info_test.go b/db/info_test.go index 12bd4666..adcdd462 100644 --- a/db/info_test.go +++ b/db/info_test.go @@ -30,6 +30,7 @@ func TestInfo(t *testing.T) { txt := ` USE NS test DB test; DEFINE LOGIN test ON NAMESPACE PASSWORD "test"; + DEFINE LOGIN test ON NAMESPACE PASSHASH "$2a$10$mofTMm4nilzRSZuU0GyuCuAgHy2mEPeNRqHZH9ETnh.O1MBjy2PDO"; DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE "test"; DEFINE DATABASE test; INFO FOR NAMESPACE; @@ -41,24 +42,25 @@ func TestInfo(t *testing.T) { res, err := Execute(setupKV(), txt, nil) So(err, ShouldBeNil) - So(res, ShouldHaveLength, 9) + So(res, ShouldHaveLength, 10) So(res[1].Status, ShouldEqual, "OK") So(res[2].Status, ShouldEqual, "OK") So(res[3].Status, ShouldEqual, "OK") So(res[4].Status, ShouldEqual, "OK") - So(data.Consume(res[4].Result[0]).Get("login").Data(), ShouldHaveLength, 1) - So(data.Consume(res[4].Result[0]).Get("login.test").Data(), ShouldEqual, "DEFINE LOGIN test ON NAMESPACE PASSWORD ********") - So(data.Consume(res[4].Result[0]).Get("token").Data(), ShouldHaveLength, 1) - So(data.Consume(res[4].Result[0]).Get("token.test").Data(), ShouldEqual, "DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE ********") - So(data.Consume(res[4].Result[0]).Get("database").Data(), ShouldHaveLength, 1) - So(data.Consume(res[4].Result[0]).Get("database.test").Data(), ShouldEqual, "DEFINE DATABASE test") So(res[5].Status, ShouldEqual, "OK") + So(data.Consume(res[5].Result[0]).Get("login").Data(), ShouldHaveLength, 1) + So(data.Consume(res[5].Result[0]).Get("login.test").Data(), ShouldEqual, `DEFINE LOGIN test ON NAMESPACE PASSHASH "$2a$10$mofTMm4nilzRSZuU0GyuCuAgHy2mEPeNRqHZH9ETnh.O1MBjy2PDO"`) + So(data.Consume(res[5].Result[0]).Get("token").Data(), ShouldHaveLength, 1) + So(data.Consume(res[5].Result[0]).Get("token.test").Data(), ShouldEqual, `DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE "test"`) + So(data.Consume(res[5].Result[0]).Get("database").Data(), ShouldHaveLength, 1) + So(data.Consume(res[5].Result[0]).Get("database.test").Data(), ShouldEqual, "DEFINE DATABASE test") So(res[6].Status, ShouldEqual, "OK") So(res[7].Status, ShouldEqual, "OK") So(res[8].Status, ShouldEqual, "OK") - So(data.Consume(res[8].Result[0]).Get("login").Data(), ShouldHaveLength, 0) - So(data.Consume(res[8].Result[0]).Get("token").Data(), ShouldHaveLength, 0) - So(data.Consume(res[8].Result[0]).Get("database").Data(), ShouldHaveLength, 0) + So(res[9].Status, ShouldEqual, "OK") + So(data.Consume(res[9].Result[0]).Get("login").Data(), ShouldHaveLength, 0) + So(data.Consume(res[9].Result[0]).Get("token").Data(), ShouldHaveLength, 0) + So(data.Consume(res[9].Result[0]).Get("database").Data(), ShouldHaveLength, 0) }) @@ -69,6 +71,7 @@ func TestInfo(t *testing.T) { txt := ` USE NS test DB test; DEFINE LOGIN test ON DATABASE PASSWORD "test"; + DEFINE LOGIN test ON DATABASE PASSHASH "$2a$10$mofTMm4nilzRSZuU0GyuCuAgHy2mEPeNRqHZH9ETnh.O1MBjy2PDO"; DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE "test"; DEFINE SCOPE test; DEFINE TABLE test; @@ -82,29 +85,30 @@ func TestInfo(t *testing.T) { res, err := Execute(setupKV(), txt, nil) So(err, ShouldBeNil) - So(res, ShouldHaveLength, 11) + So(res, ShouldHaveLength, 12) So(res[1].Status, ShouldEqual, "OK") So(res[2].Status, ShouldEqual, "OK") So(res[3].Status, ShouldEqual, "OK") So(res[4].Status, ShouldEqual, "OK") So(res[5].Status, ShouldEqual, "OK") - So(data.Consume(res[5].Result[0]).Get("login").Data(), ShouldHaveLength, 1) - So(data.Consume(res[5].Result[0]).Get("login.test").Data(), ShouldEqual, "DEFINE LOGIN test ON DATABASE PASSWORD ********") - So(data.Consume(res[5].Result[0]).Get("token").Data(), ShouldHaveLength, 1) - So(data.Consume(res[5].Result[0]).Get("token.test").Data(), ShouldEqual, "DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE ********") - So(data.Consume(res[5].Result[0]).Get("scope").Data(), ShouldHaveLength, 1) - So(data.Consume(res[5].Result[0]).Get("scope.test").Data(), ShouldEqual, "DEFINE SCOPE test") - So(data.Consume(res[5].Result[0]).Get("table").Data(), ShouldHaveLength, 1) - So(data.Consume(res[5].Result[0]).Get("table.test").Data(), ShouldEqual, "DEFINE TABLE test") So(res[6].Status, ShouldEqual, "OK") + So(data.Consume(res[6].Result[0]).Get("login").Data(), ShouldHaveLength, 1) + So(data.Consume(res[6].Result[0]).Get("login.test").Data(), ShouldEqual, `DEFINE LOGIN test ON DATABASE PASSHASH "$2a$10$mofTMm4nilzRSZuU0GyuCuAgHy2mEPeNRqHZH9ETnh.O1MBjy2PDO"`) + So(data.Consume(res[6].Result[0]).Get("token").Data(), ShouldHaveLength, 1) + So(data.Consume(res[6].Result[0]).Get("token.test").Data(), ShouldEqual, `DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE "test"`) + So(data.Consume(res[6].Result[0]).Get("scope").Data(), ShouldHaveLength, 1) + So(data.Consume(res[6].Result[0]).Get("scope.test").Data(), ShouldEqual, "DEFINE SCOPE test") + So(data.Consume(res[6].Result[0]).Get("table").Data(), ShouldHaveLength, 1) + So(data.Consume(res[6].Result[0]).Get("table.test").Data(), ShouldEqual, "DEFINE TABLE test") So(res[7].Status, ShouldEqual, "OK") So(res[8].Status, ShouldEqual, "OK") So(res[9].Status, ShouldEqual, "OK") So(res[10].Status, ShouldEqual, "OK") - So(data.Consume(res[10].Result[0]).Get("login").Data(), ShouldHaveLength, 0) - So(data.Consume(res[10].Result[0]).Get("token").Data(), ShouldHaveLength, 0) - So(data.Consume(res[10].Result[0]).Get("scope").Data(), ShouldHaveLength, 0) - So(data.Consume(res[10].Result[0]).Get("table").Data(), ShouldHaveLength, 0) + So(res[11].Status, ShouldEqual, "OK") + So(data.Consume(res[11].Result[0]).Get("login").Data(), ShouldHaveLength, 0) + So(data.Consume(res[11].Result[0]).Get("token").Data(), ShouldHaveLength, 0) + So(data.Consume(res[11].Result[0]).Get("scope").Data(), ShouldHaveLength, 0) + So(data.Consume(res[11].Result[0]).Get("table").Data(), ShouldHaveLength, 0) }) @@ -127,7 +131,7 @@ func TestInfo(t *testing.T) { So(res[1].Status, ShouldEqual, "OK") So(res[2].Status, ShouldEqual, "OK") So(data.Consume(res[3].Result[0]).Get("token").Data(), ShouldHaveLength, 1) - So(data.Consume(res[3].Result[0]).Get("token.test").Data(), ShouldEqual, "DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE ********") + So(data.Consume(res[3].Result[0]).Get("token.test").Data(), ShouldEqual, `DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE "test"`) So(res[4].Status, ShouldEqual, "OK") So(res[5].Status, ShouldEqual, "OK") So(data.Consume(res[5].Result[0]).Get("token").Data(), ShouldHaveLength, 0) diff --git a/sql/ast.go b/sql/ast.go index 10ccce03..98f044d6 100644 --- a/sql/ast.go +++ b/sql/ast.go @@ -305,6 +305,7 @@ type DefineLoginStatement struct { Kind Token User *Ident Pass []byte + Hash []byte Code []byte } diff --git a/sql/login.go b/sql/login.go index 29c6537f..21a06eb6 100644 --- a/sql/login.go +++ b/sql/login.go @@ -42,12 +42,21 @@ func (p *parser) parseDefineLoginStatement() (stmt *DefineLoginStatement, err er } } - if _, _, err := p.shouldBe(PASSWORD); err != nil { + tok, _, err := p.shouldBe(PASSWORD, PASSHASH) + if err != nil { return nil, err } - if stmt.Pass, err = p.parseBinary(); err != nil { - return nil, err + if is(tok, PASSWORD) { + if stmt.Pass, err = p.parseBinary(); err != nil { + return nil, err + } + } + + if is(tok, PASSHASH) { + if stmt.Hash, err = p.parseBinary(); err != nil { + return nil, err + } } return diff --git a/sql/sql_test.go b/sql/sql_test.go index 96cf9053..6ba1b9e6 100644 --- a/sql/sql_test.go +++ b/sql/sql_test.go @@ -2180,7 +2180,7 @@ func Test_Parse_Queries_Define(t *testing.T) { }, { sql: `DEFINE LOGIN test ON NAMESPACE`, - err: "Found `` but expected `PASSWORD`", + err: "Found `` but expected `PASSWORD, PASSHASH`", }, { sql: `DEFINE LOGIN test ON NAMESPACE PASSWORD`, @@ -2188,7 +2188,7 @@ func Test_Parse_Queries_Define(t *testing.T) { }, { sql: `DEFINE LOGIN test ON NAMESPACE PASSWORD "123456"`, - str: `DEFINE LOGIN test ON NAMESPACE PASSWORD ********`, + str: `DEFINE LOGIN test ON NAMESPACE PASSHASH "123456"`, res: &Query{Statements: []Statement{&DefineLoginStatement{ KV: "*", NS: "*", DB: "*", Kind: NAMESPACE, @@ -2198,7 +2198,7 @@ func Test_Parse_Queries_Define(t *testing.T) { }, { sql: `DEFINE LOGIN test ON DATABASE PASSWORD "123456"`, - str: `DEFINE LOGIN test ON DATABASE PASSWORD ********`, + str: `DEFINE LOGIN test ON DATABASE PASSHASH "123456"`, res: &Query{Statements: []Statement{&DefineLoginStatement{ KV: "*", NS: "*", DB: "*", Kind: DATABASE, @@ -2261,7 +2261,7 @@ func Test_Parse_Queries_Define(t *testing.T) { }, { sql: `DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE "secret"`, - str: `DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE ********`, + str: `DEFINE TOKEN test ON NAMESPACE TYPE HS512 VALUE "secret"`, res: &Query{Statements: []Statement{&DefineTokenStatement{ KV: "*", NS: "*", DB: "*", Kind: NAMESPACE, @@ -2273,7 +2273,7 @@ func Test_Parse_Queries_Define(t *testing.T) { }, { sql: `DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE "secret"`, - str: `DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE ********`, + str: `DEFINE TOKEN test ON DATABASE TYPE HS512 VALUE "secret"`, res: &Query{Statements: []Statement{&DefineTokenStatement{ KV: "*", NS: "*", DB: "*", Kind: DATABASE, @@ -2285,7 +2285,7 @@ func Test_Parse_Queries_Define(t *testing.T) { }, { sql: `DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE "secret"`, - str: `DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE ********`, + str: `DEFINE TOKEN test ON SCOPE test TYPE HS512 VALUE "secret"`, res: &Query{Statements: []Statement{&DefineTokenStatement{ KV: "*", NS: "*", DB: "*", Kind: SCOPE, diff --git a/sql/string.go b/sql/string.go index 521c3d27..e5979686 100644 --- a/sql/string.go +++ b/sql/string.go @@ -92,7 +92,7 @@ func maybe(b bool, v ...interface{}) string { } func binar(b []byte) string { - return fmt.Sprintf(`"%s"`, b) + return fmt.Sprintf("%q", b) } func quote(s string) string { @@ -321,9 +321,10 @@ func (this RemoveDatabaseStatement) String() string { } func (this DefineLoginStatement) String() string { - return print("DEFINE LOGIN %v ON %v PASSWORD ********", + return print("DEFINE LOGIN %v ON %v PASSHASH %s", this.User, this.Kind, + binar(this.Pass), ) } @@ -335,10 +336,11 @@ func (this RemoveLoginStatement) String() string { } func (this DefineTokenStatement) String() string { - return print("DEFINE TOKEN %v ON %v TYPE %v VALUE ********", + return print("DEFINE TOKEN %v ON %v TYPE %v VALUE %s", this.Name, maybe(this.Kind == SCOPE, print("%v %v", this.Kind, this.What), print("%v", this.Kind)), this.Type, + binar(this.Code), ) } diff --git a/sql/tokens.go b/sql/tokens.go index d5c23621..ce0d931d 100644 --- a/sql/tokens.go +++ b/sql/tokens.go @@ -164,6 +164,7 @@ const ( OR ORDER PARALLEL + PASSHASH PASSWORD PERMISSIONS PRIORITY @@ -339,6 +340,7 @@ var tokens = [...]string{ OR: "OR", ORDER: "ORDER", PARALLEL: "PARALLEL", + PASSHASH: "PASSHASH", PASSWORD: "PASSWORD", PERMISSIONS: "PERMISSIONS", PRIORITY: "PRIORITY",