From f35407a46bb23433cfba966e6c9deebb0e8bab97 Mon Sep 17 00:00:00 2001 From: Leon Zhang Date: Mon, 26 Nov 2018 13:41:08 +0800 Subject: [PATCH] fix #126 --- cmd/soar/tool.go | 10 ++++ database/privilege.go | 104 +++++++++++++++++++++++++++++++++++++ database/privilege_test.go | 31 +++++++++++ vendor/vendor.json | 44 ++++++++-------- 4 files changed, 167 insertions(+), 22 deletions(-) create mode 100644 database/privilege.go create mode 100644 database/privilege_test.go diff --git a/cmd/soar/tool.go b/cmd/soar/tool.go index 7e550b5..dd977d6 100644 --- a/cmd/soar/tool.go +++ b/cmd/soar/tool.go @@ -86,6 +86,11 @@ func checkConfig() int { fmt.Println("test-dsn", common.Config.TestDSN) } } + + if !testConn.HasAllPrivilege() { + fmt.Printf("test-dsn: %s, need all privileges", common.FormatDSN(common.Config.TestDSN)) + return 1 + } // OnlineDSN connection check onlineConn := &database.Connector{ Addr: common.Config.OnlineDSN.Addr, @@ -106,6 +111,11 @@ func checkConfig() int { fmt.Println("online-dsn", common.Config.OnlineDSN) } } + + if !onlineConn.HasSelectPrivilege() { + fmt.Printf("online-dsn: %s, need all privileges", common.FormatDSN(common.Config.OnlineDSN)) + return 1 + } return 0 } diff --git a/database/privilege.go b/database/privilege.go new file mode 100644 index 0000000..d66745f --- /dev/null +++ b/database/privilege.go @@ -0,0 +1,104 @@ +/* + * Copyright 2018 Xiaomi, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package database + +import ( + "errors" + "strings" + + "github.com/XiaoMi/soar/common" +) + +// CurrentUser get current user with current_user() function +func (db *Connector) CurrentUser() (string, string, error) { + res, err := db.Query("select current_user()") + if err != nil { + return "", "", err + } + if len(res.Rows) > 0 { + cols := strings.Split(res.Rows[0].Str(0), "@") + if len(cols) == 2 { + user := strings.Trim(cols[0], "'") + host := strings.Trim(cols[1], "'") + if strings.Contains(user, "'") || strings.Contains(host, "'") { + return "", "", errors.New("user or host contains irregular character") + } + return user, host, nil + } + return "", "", errors.New("user or host contains irregular character") + } + return "", "", errors.New("no privilege info") +} + +// HasSelectPrivilege if user has select privilege +func (db *Connector) HasSelectPrivilege() bool { + user, host, err := db.CurrentUser() + if err != nil { + common.Log.Error("User: %s, HasSelectPrivilege: %s", db.User, err.Error()) + return false + } + res, err := db.Query("select Select_priv from mysql.user where user='%s' and host='%s'", user, host) + if err != nil { + common.Log.Error("HasSelectPrivilege, DSN: %s, Error: %s", db.Addr, err.Error()) + return false + } + // Select_priv + if len(res.Rows) > 0 { + if res.Rows[0].Str(0) == "Y" { + return true + } + } + return false +} + +// HasAllPrivilege if user has all privileges +func (db *Connector) HasAllPrivilege() bool { + user, host, err := db.CurrentUser() + if err != nil { + common.Log.Error("User: %s, HasAllPrivilege: %s", db.User, err.Error()) + return false + } + + // concat privilege columns + res, err := db.Query("SELECT GROUP_CONCAT(COLUMN_NAME) from information_schema.COLUMNS where TABLE_SCHEMA='mysql' and TABLE_NAME='user' and COLUMN_NAME like '%_priv'") + if err != nil { + common.Log.Error("HasAllPrivilege, DSN: %s, Error: %s", db.Addr, err.Error()) + return false + } + var priv string + if len(res.Rows) > 0 { + priv = res.Rows[0].Str(0) + } else { + common.Log.Error("HasAllPrivilege, DSN: %s, get privilege string error", db.Addr) + return false + } + + // get all privilege status + res, err = db.Query("select concat("+priv+") from mysql.user where user='%s' and host='%s'", user, host) + if err != nil { + common.Log.Error("HasAllPrivilege, DSN: %s, Error: %s", db.Addr, err.Error()) + return false + } + + // %_priv + if len(res.Rows) > 0 { + if strings.Replace(res.Rows[0].Str(0), "Y", "", -1) == "" { + return true + } + } + return false +} diff --git a/database/privilege_test.go b/database/privilege_test.go new file mode 100644 index 0000000..4ed0fbc --- /dev/null +++ b/database/privilege_test.go @@ -0,0 +1,31 @@ +/* + * Copyright 2018 Xiaomi, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package database + +import "testing" + +func TestHasSelectPrivilege(t *testing.T) { + if !connTest.HasSelectPrivilege() { + t.Errorf("DSN: %s, User: %s, should has select privilege", connTest.Addr, connTest.User) + } +} + +func TestHasAllPrivilege(t *testing.T) { + if !connTest.HasAllPrivilege() { + t.Errorf("DSN: %s, User: %s, should has all privilege", connTest.Addr, connTest.User) + } +} diff --git a/vendor/vendor.json b/vendor/vendor.json index ce45bcd..a65e8ab 100644 --- a/vendor/vendor.json +++ b/vendor/vendor.json @@ -1034,68 +1034,68 @@ { "checksumSHA1": "q7Bd5YJHsxvzEpiOBaYn+wEpqyU=", "path": "vitess.io/vitess", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=", "path": "vitess.io/vitess/go/bytes2", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=", "path": "vitess.io/vitess/go/hack", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "e1WJ7vCnVrlQQQlc6n/FewCDMso=", "path": "vitess.io/vitess/go/sqltypes", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=", "path": "vitess.io/vitess/go/vt/log", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "XozR8bmeSR5KTe/nlUJkpJY2HKI=", "path": "vitess.io/vitess/go/vt/proto/query", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "OnWsUHLDKcO3spwH0jD55SvKD24=", "path": "vitess.io/vitess/go/vt/proto/topodata", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "sBAuZ/itMR8U8qbK4yLHxkP6Cpc=", "path": "vitess.io/vitess/go/vt/proto/vtgate", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "pLWM+SPGZs3k+IhjktE/cGUlpM0=", "path": "vitess.io/vitess/go/vt/proto/vtrpc", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "2ZBC/pPjs13cocUf8PoMSvAO5u4=", "path": "vitess.io/vitess/go/vt/sqlparser", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" }, { "checksumSHA1": "oF4XzuOzwvj1iduX/lYqNSyY/HM=", "path": "vitess.io/vitess/go/vt/vterrors", - "revision": "32dd398dd5459e5cf15904db35aba7460519b612", - "revisionTime": "2018-11-22T02:19:16Z" + "revision": "9d0594c50251a5167ac786408cea822ae994951c", + "revisionTime": "2018-11-25T05:49:41Z" } ], "rootPath": "github.com/XiaoMi/soar" -- GitLab