forked from pingcap/tidb-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
privilege.go
133 lines (118 loc) · 3.92 KB
/
privilege.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
// Copyright 2018 PingCAP, 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,
// See the License for the specific language governing permissions and
// limitations under the License.
package check
import (
"context"
"database/sql"
"fmt"
"strings"
"github.com/pingcap/errors"
"github.com/pingcap/parser"
"github.com/pingcap/parser/ast"
"github.com/pingcap/tidb-tools/pkg/dbutil"
_ "github.com/pingcap/tidb/types/parser_driver" // for parser driver
)
/*****************************************************/
// SourcePrivilegeChecker checks data source privileges.
type SourcePrivilegeChecker struct {
db *sql.DB
dbinfo *dbutil.DBConfig
}
// NewSourcePrivilegeChecker returns a Checker.
func NewSourcePrivilegeChecker(db *sql.DB, dbinfo *dbutil.DBConfig) Checker {
return &SourcePrivilegeChecker{db: db, dbinfo: dbinfo}
}
// Check implements the Checker interface.
// We only check REPLICATION SLAVE, REPLICATION CLIENT, RELOAD privileges.
// REPLICATION SLAVE and REPLICATION CLIENT are required.
// RELOAD is strongly suggested to have.
func (pc *SourcePrivilegeChecker) Check(ctx context.Context) *Result {
result := &Result{
Name: pc.Name(),
Desc: "check privileges of source DB",
State: StateFailure,
Extra: fmt.Sprintf("address of db instance - %s:%d", pc.dbinfo.Host, pc.dbinfo.Port),
}
grants, err := dbutil.ShowGrants(ctx, pc.db, "", "")
if err != nil {
markCheckError(result, err)
return result
}
if len(grants) == 0 {
result.ErrorMsg = "there is no such grant defined for current user on host '%'"
return result
}
// get username and hostname
node, err := parser.New().ParseOneStmt(grants[0], "", "")
if err != nil {
result.ErrorMsg = errors.ErrorStack(errors.Annotatef(err, "grants[0] %s", grants[0]))
return result
}
grantStmt, ok := node.(*ast.GrantStmt)
if !ok {
result.ErrorMsg = fmt.Sprintf("%s is not grant stament", grants[0])
return result
}
if len(grantStmt.Users) == 0 {
result.ErrorMsg = fmt.Sprintf("grant has not user %s", grantStmt.Text())
return result
}
var (
hasPrivilegeOfReplicationSlave bool
hasPrivilegeOfReplicationClient bool
hasPrivilegeOfReload bool
)
// TODO: user tidb parser(which not works very well now)
for _, grant := range grants {
if strings.Contains(grant, "ALL PRIVILEGES") {
result.State = StateSuccess
return result
}
if strings.Contains(grant, "REPLICATION SLAVE") {
hasPrivilegeOfReplicationSlave = true
}
if strings.Contains(grant, "REPLICATION CLIENT") {
hasPrivilegeOfReplicationClient = true
}
if strings.Contains(grant, "RELOAD") {
hasPrivilegeOfReload = true
}
}
user := grantStmt.Users[0]
lackOfPrivileges := make([]string, 0, 3)
if !hasPrivilegeOfReplicationSlave {
lackOfPrivileges = append(lackOfPrivileges, "REPLICATION SLAVE")
}
if !hasPrivilegeOfReplicationClient {
lackOfPrivileges = append(lackOfPrivileges, "REPLICATION CLIENT")
}
if !hasPrivilegeOfReload {
lackOfPrivileges = append(lackOfPrivileges, "RELOAD")
}
if len(lackOfPrivileges) != 0 {
privileges := strings.Join(lackOfPrivileges, ",")
result.ErrorMsg = fmt.Sprintf("lack of %s privilege", privileges)
result.Instruction = fmt.Sprintf("GRANT %s ON *.* TO '%s'@'%s';", privileges, user.User.Username, "%")
if !hasPrivilegeOfReload && hasPrivilegeOfReplicationClient && hasPrivilegeOfReplicationSlave {
result.State = StateWarning
result.Instruction += "Or you use no-lock option to dump data in next step"
}
return result
}
result.State = StateSuccess
return result
}
// Name implements the Checker interface.
func (pc *SourcePrivilegeChecker) Name() string {
return "source_db_privilege"
}