github.com/pingcap/tiflow@v0.0.0-20240520035814-5bf52d54e205/dm/pkg/checker/privilege_test.go (about) 1 // Copyright 2021 PingCAP, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // See the License for the specific language governing permissions and 12 // limitations under the License. 13 14 package checker 15 16 import ( 17 "testing" 18 19 tc "github.com/pingcap/check" 20 "github.com/pingcap/tidb/pkg/parser/mysql" 21 "github.com/pingcap/tidb/pkg/util/filter" 22 "github.com/stretchr/testify/require" 23 ) 24 25 func TestClient(t *testing.T) { 26 tc.TestingT(t) 27 } 28 29 var _ = tc.Suite(&testCheckSuite{}) 30 31 type testCheckSuite struct{} 32 33 func TestVerifyDumpPrivileges(t *testing.T) { 34 cases := []struct { 35 grants []string 36 checkTables []filter.Table 37 dumpWholeInstance bool 38 dumpState State 39 errStr string 40 }{ 41 { 42 grants: nil, // non grants 43 dumpState: StateFailure, 44 errStr: "there is no such grant defined for current user on host '%'", 45 }, 46 { 47 grants: []string{"invalid SQL statement"}, 48 dumpState: StateFailure, 49 errStr: "line 1 column 7 near \"invalid SQL statement\" ", 50 }, 51 { 52 grants: []string{"CREATE DATABASE db1"}, // non GRANT statement 53 dumpState: StateFailure, 54 errStr: "CREATE DATABASE db1 is not grant statement", 55 }, 56 { 57 grants: []string{"GRANT RELOAD ON *.* TO 'user'@'%'"}, // lack SELECT privilege 58 dumpState: StateFailure, 59 checkTables: []filter.Table{ 60 {Schema: "db1", Name: "tb1"}, 61 }, 62 errStr: "lack of Select privilege: {`db1`.`tb1`}; ", 63 }, 64 { 65 grants: []string{"GRANT RELOAD ON *.* TO 'user'@'%'"}, // lack SELECT privilege but no do-tables 66 dumpState: StateSuccess, 67 }, 68 { 69 grants: []string{ // lack optional privilege 70 "GRANT RELOAD ON *.* TO 'user'@'%'", 71 "GRANT EXECUTE ON FUNCTION db1.anomaly_score TO 'user1'@'domain-or-ip-address1'", 72 }, 73 dumpState: StateFailure, 74 checkTables: []filter.Table{ 75 {Schema: "db1", Name: "anomaly_score"}, 76 }, 77 errStr: "lack of Select privilege: {`db1`.`anomaly_score`}; ", 78 }, 79 { 80 grants: []string{ // have privileges 81 "GRANT RELOAD, SELECT ON *.* TO 'user'@'%'", 82 }, 83 dumpState: StateSuccess, 84 }, 85 { 86 grants: []string{ // have privileges 87 "GRANT ALL PRIVILEGES ON *.* TO 'user'@'%'", 88 }, 89 dumpState: StateSuccess, 90 }, 91 { 92 grants: []string{ // lower case 93 "GRANT all privileges ON *.* TO 'user'@'%'", 94 }, 95 dumpState: StateSuccess, 96 }, 97 { 98 grants: []string{ // IDENTIFIED BY PASSWORD 99 "GRANT ALL PRIVILEGES ON *.* TO 'user'@'%' IDENTIFIED BY PASSWORD 'secret'", 100 }, 101 dumpState: StateSuccess, 102 }, 103 { 104 grants: []string{ // IDENTIFIED BY PASSWORD 105 "GRANT ALL PRIVILEGES ON *.* TO 'user'@'%' IDENTIFIED BY PASSWORD 'password' WITH GRANT OPTION", 106 }, 107 dumpState: StateSuccess, 108 }, 109 { 110 grants: []string{ // Aurora have `LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA` 111 "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA, INVOKE SAGEMAKER, INVOKE COMPREHEND ON *.* TO 'root'@'%' WITH GRANT OPTION", 112 }, 113 dumpState: StateSuccess, 114 }, 115 { 116 grants: []string{ // Aurora have `LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA` 117 "GRANT INSERT, UPDATE, DELETE, CREATE, DROP, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA, INVOKE SAGEMAKER, INVOKE COMPREHEND ON *.* TO 'root'@'%' WITH GRANT OPTION", 118 }, 119 checkTables: []filter.Table{ 120 {Schema: "db1", Name: "tb1"}, 121 }, 122 dumpState: StateFailure, 123 errStr: "lack of Select privilege: {`db1`.`tb1`}; lack of RELOAD global (*.*) privilege; ", 124 }, 125 { 126 grants: []string{ // test `LOAD FROM S3, SELECT INTO S3` not at end 127 "GRANT INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, LOAD FROM S3, SELECT INTO S3, SELECT ON *.* TO 'root'@'%' WITH GRANT OPTION", 128 }, 129 checkTables: []filter.Table{ 130 {Schema: "db1", Name: "tb1"}, 131 }, 132 dumpState: StateSuccess, 133 }, 134 { 135 grants: []string{ // ... and `LOAD FROM S3` at beginning, as well as not adjacent with `SELECT INTO S3` 136 "GRANT LOAD FROM S3, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, SELECT INTO S3, SELECT ON *.* TO 'root'@'%' WITH GRANT OPTION", 137 }, 138 dumpState: StateSuccess, 139 }, 140 { 141 grants: []string{ // lack db/table level privilege 142 "GRANT ALL PRIVILEGES ON `medz`.* TO `zhangsan`@`10.8.1.9` WITH GRANT OPTION", 143 }, 144 dumpState: StateFailure, 145 checkTables: []filter.Table{ 146 {Schema: "medz", Name: "medz"}, 147 }, 148 errStr: "lack of RELOAD global (*.*) privilege; ", 149 }, 150 { 151 grants: []string{ // privilege on db/table level is not enough to execute SHOW MASTER STATUS 152 "GRANT ALL PRIVILEGES ON `medz`.* TO `zhangsan`@`10.8.1.9` WITH GRANT OPTION", 153 }, 154 dumpState: StateFailure, 155 checkTables: []filter.Table{ 156 {Schema: "medz", Name: "medz"}, 157 }, 158 errStr: "lack of RELOAD global (*.*) privilege; ", 159 }, 160 { 161 grants: []string{ // privilege on column level is not enough to execute SHOW CREATE TABLE 162 "GRANT RELOAD ON *.* TO 'user'@'%'", 163 "GRANT SELECT (c) ON `lance`.`t` TO 'user'@'%'", 164 }, 165 dumpState: StateFailure, 166 checkTables: []filter.Table{ 167 {Schema: "lance", Name: "t"}, 168 }, 169 errStr: "lack of Select privilege: {`lance`.`t`}; ", 170 }, 171 { 172 grants: []string{ 173 "GRANT RELOAD ON *.* TO `u1`@`localhost`", 174 "GRANT SELECT ON `db1`.* TO `u1`@`localhost`", 175 "GRANT `r1`@`%`,`r2`@`%` TO `u1`@`localhost`", 176 }, 177 dumpState: StateSuccess, 178 checkTables: []filter.Table{ 179 {Schema: "db1", Name: "t"}, 180 }, 181 }, 182 { 183 grants: []string{ 184 "GRANT RELOAD ON *.* TO `u1`@`localhost`", 185 "GRANT SELECT ON `db1`.* TO `u1`@`localhost`", 186 "GRANT `r1`@`%`,`r2`@`%` TO `u1`@`localhost`", 187 }, 188 dumpState: StateFailure, 189 dumpWholeInstance: true, 190 errStr: "lack of Select global (*.*) privilege; ", 191 }, 192 { 193 grants: []string{ 194 "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, SHUTDOWN, PROCESS, FILE, REFERENCES, INDEX, ALTER, SHOW DATABASES, SUPER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, CREATE TABLESPACE, CREATE ROLE, DROP ROLE ON *.* TO `root`@`localhost` WITH GRANT OPTION", 195 "GRANT APPLICATION_PASSWORD_ADMIN,AUDIT_ADMIN,BACKUP_ADMIN,BINLOG_ADMIN,BINLOG_ENCRYPTION_ADMIN,CLONE_ADMIN,CONNECTION_ADMIN,ENCRYPTION_KEY_ADMIN,GROUP_REPLICATION_ADMIN,INNODB_REDO_LOG_ARCHIVE,PERSIST_RO_VARIABLES_ADMIN,REPLICATION_APPLIER,REPLICATION_SLAVE_ADMIN,RESOURCE_GROUP_ADMIN,RESOURCE_GROUP_USER,ROLE_ADMIN,SERVICE_CONNECTION_ADMIN,SESSION_VARIABLES_ADMIN,SET_USER_ID,SYSTEM_USER,SYSTEM_VARIABLES_ADMIN,TABLE_ENCRYPTION_ADMIN,XA_RECOVER_ADMIN ON *.* TO `root`@`localhost` WITH GRANT OPTION", 196 "GRANT PROXY ON ''@'' TO 'root'@'localhost' WITH GRANT OPTION", 197 }, 198 dumpState: StateSuccess, 199 }, 200 } 201 202 for _, cs := range cases { 203 result := &Result{ 204 State: StateFailure, 205 } 206 dumpRequiredPrivs := map[mysql.PrivilegeType]priv{ 207 mysql.SelectPriv: { 208 needGlobal: false, 209 dbs: genTableLevelPrivs(cs.checkTables), 210 }, 211 mysql.ReloadPriv: {needGlobal: true}, 212 } 213 if cs.dumpWholeInstance { 214 dumpRequiredPrivs[mysql.SelectPriv] = priv{needGlobal: true} 215 } 216 err := verifyPrivilegesWithResult(result, cs.grants, dumpRequiredPrivs) 217 if cs.dumpState == StateSuccess { 218 require.Nil(t, err, "grants: %v", cs.grants) 219 } else { 220 require.NotNil(t, err, "grants: %v", cs.grants) 221 require.Equal(t, cs.errStr, err.ShortErr, "grants: %v", cs.grants) 222 } 223 } 224 } 225 226 func TestVerifyReplicationPrivileges(t *testing.T) { 227 cases := []struct { 228 grants []string 229 checkTables []*filter.Table 230 replicationState State 231 errStr string 232 }{ 233 { 234 grants: nil, // non grants 235 replicationState: StateFailure, 236 errStr: "there is no such grant defined for current user on host '%'", 237 }, 238 { 239 grants: []string{"invalid SQL statement"}, 240 replicationState: StateFailure, 241 errStr: "line 1 column 7 near \"invalid SQL statement\" ", 242 }, 243 { 244 grants: []string{"CREATE DATABASE db1"}, // non GRANT statement 245 replicationState: StateFailure, 246 errStr: "CREATE DATABASE db1 is not grant statement", 247 }, 248 { 249 grants: []string{"GRANT SELECT ON *.* TO 'user'@'%'"}, // lack necessary privilege 250 replicationState: StateFailure, 251 errStr: "lack of REPLICATION CLIENT global (*.*) privilege; lack of REPLICATION SLAVE global (*.*) privilege; ", 252 }, 253 { 254 grants: []string{"GRANT REPLICATION SLAVE ON *.* TO 'user'@'%'"}, // lack REPLICATION CLIENT privilege 255 replicationState: StateFailure, 256 errStr: "lack of REPLICATION CLIENT global (*.*) privilege; ", 257 }, 258 { 259 grants: []string{"GRANT REPLICATION CLIENT ON *.* TO 'user'@'%'"}, // lack REPLICATION SLAVE privilege 260 replicationState: StateFailure, 261 errStr: "lack of REPLICATION SLAVE global (*.*) privilege; ", 262 }, 263 { 264 grants: []string{ // have privileges 265 "GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'user'@'%'", 266 }, 267 replicationState: StateSuccess, 268 }, 269 { 270 grants: []string{ // have privileges 271 "GRANT ALL PRIVILEGES ON *.* TO 'user'@'%'", 272 }, 273 replicationState: StateSuccess, 274 }, 275 { 276 grants: []string{ // lower case 277 "GRANT all privileges ON *.* TO 'user'@'%'", 278 }, 279 replicationState: StateSuccess, 280 }, 281 { 282 grants: []string{ // IDENTIFIED BY PASSWORD 283 "GRANT ALL PRIVILEGES ON *.* TO 'user'@'%' IDENTIFIED BY PASSWORD 'secret'", 284 }, 285 replicationState: StateSuccess, 286 }, 287 { 288 grants: []string{ // IDENTIFIED BY PASSWORD 289 "GRANT ALL PRIVILEGES ON *.* TO 'user'@'%' IDENTIFIED BY PASSWORD 'password' WITH GRANT OPTION", 290 }, 291 replicationState: StateSuccess, 292 }, 293 { 294 grants: []string{ // Aurora have `LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA` 295 "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA, INVOKE SAGEMAKER, INVOKE COMPREHEND ON *.* TO 'root'@'%' WITH GRANT OPTION", 296 }, 297 replicationState: StateSuccess, 298 }, 299 { 300 grants: []string{ // Aurora have `LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA` 301 "GRANT INSERT, UPDATE, DELETE, CREATE, DROP, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, LOAD FROM S3, SELECT INTO S3, INVOKE LAMBDA, INVOKE SAGEMAKER, INVOKE COMPREHEND ON *.* TO 'root'@'%' WITH GRANT OPTION", 302 }, 303 replicationState: StateFailure, 304 errStr: "lack of REPLICATION CLIENT global (*.*) privilege; lack of REPLICATION SLAVE global (*.*) privilege; ", 305 }, 306 { 307 grants: []string{ // test `LOAD FROM S3, SELECT INTO S3` not at end 308 "GRANT INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, LOAD FROM S3, SELECT INTO S3, SELECT ON *.* TO 'root'@'%' WITH GRANT OPTION", 309 }, 310 replicationState: StateSuccess, 311 }, 312 { 313 grants: []string{ // ... and `LOAD FROM S3` at beginning, as well as not adjacent with `SELECT INTO S3` 314 "GRANT LOAD FROM S3, INSERT, UPDATE, DELETE, CREATE, DROP, RELOAD, PROCESS, REFERENCES, INDEX, ALTER, SHOW DATABASES, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, REPLICATION SLAVE, REPLICATION CLIENT, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, CREATE USER, EVENT, TRIGGER, SELECT INTO S3, SELECT ON *.* TO 'root'@'%' WITH GRANT OPTION", 315 }, 316 replicationState: StateSuccess, 317 }, 318 } 319 320 for _, cs := range cases { 321 result := &Result{ 322 State: StateFailure, 323 } 324 replRequiredPrivs := map[mysql.PrivilegeType]priv{ 325 mysql.ReplicationSlavePriv: {needGlobal: true}, 326 mysql.ReplicationClientPriv: {needGlobal: true}, 327 } 328 err := verifyPrivilegesWithResult(result, cs.grants, replRequiredPrivs) 329 if cs.replicationState == StateSuccess { 330 require.Nil(t, err, "grants: %v", cs.grants) 331 } else { 332 require.NotNil(t, err, "grants: %v", cs.grants) 333 require.Equal(t, cs.errStr, err.ShortErr, "grants: %v", cs.grants) 334 } 335 } 336 } 337 338 func TestVerifyPrivilegesWildcard(t *testing.T) { 339 cases := []struct { 340 grants []string 341 checkTables []filter.Table 342 replicationState State 343 errStr string 344 }{ 345 { 346 grants: []string{ 347 "GRANT SELECT ON `demo\\_foobar`.* TO `dmuser`@`%`", 348 }, 349 checkTables: []filter.Table{ 350 {Schema: "demo_foobar", Name: "t1"}, 351 }, 352 replicationState: StateSuccess, 353 }, 354 { 355 grants: []string{ 356 "GRANT SELECT ON `demo\\_foobar`.* TO `dmuser`@`%`", 357 }, 358 checkTables: []filter.Table{ 359 {Schema: "demo2foobar", Name: "t1"}, 360 }, 361 replicationState: StateFailure, 362 errStr: "lack of Select privilege: {`demo2foobar`.`t1`}; ", 363 }, 364 { 365 grants: []string{ 366 "GRANT SELECT ON `demo_`.* TO `dmuser`@`%`", 367 }, 368 checkTables: []filter.Table{ 369 {Schema: "demo1", Name: "t1"}, 370 {Schema: "demo2", Name: "t1"}, 371 }, 372 replicationState: StateSuccess, 373 }, 374 { 375 grants: []string{ 376 "GRANT SELECT ON `demo%`.* TO `dmuser`@`%`", 377 }, 378 checkTables: []filter.Table{ 379 {Schema: "demo_some", Name: "t1"}, 380 {Schema: "block_db", Name: "t1"}, 381 }, 382 replicationState: StateFailure, 383 errStr: "lack of Select privilege: {`block_db`.`t1`}; ", 384 }, 385 { 386 grants: []string{ 387 "GRANT SELECT ON `demo_db`.`t1` TO `dmuser`@`%`", 388 }, 389 checkTables: []filter.Table{ 390 {Schema: "demo_db", Name: "t1"}, 391 {Schema: "demo2db", Name: "t1"}, 392 }, 393 replicationState: StateFailure, 394 errStr: "lack of Select privilege: {`demo2db`.`t1`}; ", 395 }, 396 } 397 398 for i, cs := range cases { 399 t.Logf("case %d", i) 400 result := &Result{ 401 State: StateFailure, 402 } 403 requiredPrivs := map[mysql.PrivilegeType]priv{ 404 mysql.SelectPriv: { 405 dbs: genTableLevelPrivs(cs.checkTables), 406 }, 407 } 408 err := verifyPrivilegesWithResult(result, cs.grants, requiredPrivs) 409 if cs.replicationState == StateSuccess { 410 require.Nil(t, err, "grants: %v", cs.grants) 411 } else { 412 require.NotNil(t, err, "grants: %v", cs.grants) 413 require.Equal(t, cs.errStr, err.ShortErr, "grants: %v", cs.grants) 414 } 415 } 416 } 417 418 func TestVerifyTargetPrivilege(t *testing.T) { 419 cases := []struct { 420 grants []string 421 checkState State 422 errStr string 423 }{ 424 { 425 grants: nil, // non grants 426 checkState: StateWarning, 427 errStr: "there is no such grant defined for current user on host '%'", 428 }, 429 { 430 grants: []string{"invalid SQL statement"}, 431 checkState: StateWarning, 432 errStr: "line 1 column 7 near \"invalid SQL statement\" ", 433 }, 434 { 435 grants: []string{"CREATE DATABASE db1"}, // non GRANT statement 436 checkState: StateWarning, 437 errStr: "CREATE DATABASE db1 is not grant statement", 438 }, 439 { 440 grants: []string{ 441 "GRANT ALL PRIVILEGES ON *.* TO 'user'@'%'", 442 }, 443 checkState: StateSuccess, 444 }, 445 { 446 grants: []string{ 447 "GRANT SELECT, CREATE, INSERT, UPDATE, DELETE, ALTER, DROP ON *.* TO 'root'@'%'", 448 }, 449 checkState: StateSuccess, 450 }, 451 { 452 grants: []string{ 453 "GRANT SELECT, INSERT, DELETE, ALTER, DROP ON *.* TO 'root'@'%'", 454 }, 455 checkState: StateWarning, 456 errStr: "lack of Create global (*.*) privilege; lack of Update global (*.*) privilege; ", 457 }, 458 } 459 for _, cs := range cases { 460 result := &Result{ 461 State: StateWarning, 462 } 463 replRequiredPrivs := map[mysql.PrivilegeType]priv{ 464 mysql.CreatePriv: {needGlobal: true}, 465 mysql.SelectPriv: {needGlobal: true}, 466 mysql.InsertPriv: {needGlobal: true}, 467 mysql.UpdatePriv: {needGlobal: true}, 468 mysql.DeletePriv: {needGlobal: true}, 469 mysql.AlterPriv: {needGlobal: true}, 470 mysql.DropPriv: {needGlobal: true}, 471 } 472 err := verifyPrivilegesWithResult(result, cs.grants, replRequiredPrivs) 473 if cs.checkState == StateSuccess { 474 require.Nil(t, err, "grants: %v", cs.grants) 475 } else { 476 require.NotNil(t, err, "grants: %v", cs.grants) 477 require.Equal(t, cs.errStr, err.ShortErr, "grants: %v", cs.grants) 478 } 479 } 480 }