github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/executor/grant.go (about) 1 // Copyright 2016 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 executor 15 16 import ( 17 "fmt" 18 "strings" 19 20 "github.com/insionng/yougam/libraries/juju/errors" 21 "github.com/insionng/yougam/libraries/pingcap/tidb/ast" 22 "github.com/insionng/yougam/libraries/pingcap/tidb/column" 23 "github.com/insionng/yougam/libraries/pingcap/tidb/context" 24 "github.com/insionng/yougam/libraries/pingcap/tidb/model" 25 "github.com/insionng/yougam/libraries/pingcap/tidb/mysql" 26 "github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx" 27 "github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/db" 28 "github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/variable" 29 "github.com/insionng/yougam/libraries/pingcap/tidb/table" 30 "github.com/insionng/yougam/libraries/pingcap/tidb/util/sqlexec" 31 "github.com/insionng/yougam/libraries/pingcap/tidb/util/types" 32 ) 33 34 /*** 35 * Grant Statement 36 * See: https://dev.mysql.com/doc/refman/5.7/en/grant.html 37 ************************************************************************************/ 38 var ( 39 _ Executor = (*GrantExec)(nil) 40 ) 41 42 // GrantExec executes GrantStmt. 43 type GrantExec struct { 44 Privs []*ast.PrivElem 45 ObjectType ast.ObjectTypeType 46 Level *ast.GrantLevel 47 Users []*ast.UserSpec 48 49 ctx context.Context 50 done bool 51 } 52 53 // Fields implements Executor Fields interface. 54 func (e *GrantExec) Fields() []*ast.ResultField { 55 return nil 56 } 57 58 // Next implements Execution Next interface. 59 func (e *GrantExec) Next() (*Row, error) { 60 if e.done { 61 return nil, nil 62 } 63 // Grant for each user 64 for _, user := range e.Users { 65 // Check if user exists. 66 userName, host := parseUser(user.User) 67 exists, err := userExists(e.ctx, userName, host) 68 if err != nil { 69 return nil, errors.Trace(err) 70 } 71 if !exists { 72 return nil, errors.Errorf("Unknown user: %s", user.User) 73 } 74 75 // If there is no privilege entry in corresponding table, insert a new one. 76 // DB scope: mysql.DB 77 // Table scope: mysql.Tables_priv 78 // Column scope: mysql.Columns_priv 79 switch e.Level.Level { 80 case ast.GrantLevelDB: 81 err := e.checkAndInitDBPriv(userName, host) 82 if err != nil { 83 return nil, errors.Trace(err) 84 } 85 case ast.GrantLevelTable: 86 err := e.checkAndInitTablePriv(userName, host) 87 if err != nil { 88 return nil, errors.Trace(err) 89 } 90 } 91 // Grant each priv to the user. 92 for _, priv := range e.Privs { 93 if len(priv.Cols) > 0 { 94 // Check column scope privilege entry. 95 // TODO: Check validity before insert new entry. 96 err1 := e.checkAndInitColumnPriv(userName, host, priv.Cols) 97 if err1 != nil { 98 return nil, errors.Trace(err1) 99 } 100 } 101 err2 := e.grantPriv(priv, user) 102 if err2 != nil { 103 return nil, errors.Trace(err2) 104 } 105 } 106 } 107 e.done = true 108 return nil, nil 109 } 110 111 // Close implements Executor Close interface. 112 func (e *GrantExec) Close() error { 113 return nil 114 } 115 116 // Check if DB scope privilege entry exists in mysql.DB. 117 // If unexists, insert a new one. 118 func (e *GrantExec) checkAndInitDBPriv(user string, host string) error { 119 db, err := e.getTargetSchema() 120 if err != nil { 121 return errors.Trace(err) 122 } 123 ok, err := dbUserExists(e.ctx, user, host, db.Name.O) 124 if err != nil { 125 return errors.Trace(err) 126 } 127 if ok { 128 return nil 129 } 130 // Entry does not exist for user-host-db. Insert a new entry. 131 return initDBPrivEntry(e.ctx, user, host, db.Name.O) 132 } 133 134 // Check if table scope privilege entry exists in mysql.Tables_priv. 135 // If unexists, insert a new one. 136 func (e *GrantExec) checkAndInitTablePriv(user string, host string) error { 137 db, tbl, err := e.getTargetSchemaAndTable() 138 if err != nil { 139 return errors.Trace(err) 140 } 141 ok, err := tableUserExists(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O) 142 if err != nil { 143 return errors.Trace(err) 144 } 145 if ok { 146 return nil 147 } 148 // Entry does not exist for user-host-db-tbl. Insert a new entry. 149 return initTablePrivEntry(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O) 150 } 151 152 // Check if column scope privilege entry exists in mysql.Columns_priv. 153 // If unexists, insert a new one. 154 func (e *GrantExec) checkAndInitColumnPriv(user string, host string, cols []*ast.ColumnName) error { 155 db, tbl, err := e.getTargetSchemaAndTable() 156 if err != nil { 157 return errors.Trace(err) 158 } 159 for _, c := range cols { 160 col := column.FindCol(tbl.Cols(), c.Name.L) 161 if col == nil { 162 return errors.Errorf("Unknown column: %s", c.Name.O) 163 } 164 ok, err := columnPrivEntryExists(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O, col.Name.O) 165 if err != nil { 166 return errors.Trace(err) 167 } 168 if ok { 169 continue 170 } 171 // Entry does not exist for user-host-db-tbl-col. Insert a new entry. 172 err = initColumnPrivEntry(e.ctx, user, host, db.Name.O, tbl.Meta().Name.O, col.Name.O) 173 if err != nil { 174 return errors.Trace(err) 175 } 176 } 177 return nil 178 } 179 180 // Insert a new row into mysql.DB with empty privilege. 181 func initDBPrivEntry(ctx context.Context, user string, host string, db string) error { 182 sql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, DB) VALUES ("%s", "%s", "%s")`, mysql.SystemDB, mysql.DBTable, host, user, db) 183 _, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) 184 return errors.Trace(err) 185 } 186 187 // Insert a new row into mysql.Tables_priv with empty privilege. 188 func initTablePrivEntry(ctx context.Context, user string, host string, db string, tbl string) error { 189 sql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, DB, Table_name, Table_priv, Column_priv) VALUES ("%s", "%s", "%s", "%s", "", "")`, mysql.SystemDB, mysql.TablePrivTable, host, user, db, tbl) 190 _, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) 191 return errors.Trace(err) 192 } 193 194 // Insert a new row into mysql.Columns_priv with empty privilege. 195 func initColumnPrivEntry(ctx context.Context, user string, host string, db string, tbl string, col string) error { 196 sql := fmt.Sprintf(`INSERT INTO %s.%s (Host, User, DB, Table_name, Column_name, Column_priv) VALUES ("%s", "%s", "%s", "%s", "%s", "")`, mysql.SystemDB, mysql.ColumnPrivTable, host, user, db, tbl, col) 197 _, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) 198 return errors.Trace(err) 199 } 200 201 // Grant priv to user in s.Level scope. 202 func (e *GrantExec) grantPriv(priv *ast.PrivElem, user *ast.UserSpec) error { 203 switch e.Level.Level { 204 case ast.GrantLevelGlobal: 205 return e.grantGlobalPriv(priv, user) 206 case ast.GrantLevelDB: 207 return e.grantDBPriv(priv, user) 208 case ast.GrantLevelTable: 209 if len(priv.Cols) == 0 { 210 return e.grantTablePriv(priv, user) 211 } 212 return e.grantColumnPriv(priv, user) 213 default: 214 return errors.Errorf("Unknown grant level: %#v", e.Level) 215 } 216 } 217 218 // Manipulate mysql.user table. 219 func (e *GrantExec) grantGlobalPriv(priv *ast.PrivElem, user *ast.UserSpec) error { 220 asgns, err := composeGlobalPrivUpdate(priv.Priv) 221 if err != nil { 222 return errors.Trace(err) 223 } 224 userName, host := parseUser(user.User) 225 sql := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE User="%s" AND Host="%s"`, mysql.SystemDB, mysql.UserTable, asgns, userName, host) 226 _, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql) 227 return errors.Trace(err) 228 } 229 230 // Manipulate mysql.db table. 231 func (e *GrantExec) grantDBPriv(priv *ast.PrivElem, user *ast.UserSpec) error { 232 db, err := e.getTargetSchema() 233 if err != nil { 234 return errors.Trace(err) 235 } 236 asgns, err := composeDBPrivUpdate(priv.Priv) 237 if err != nil { 238 return errors.Trace(err) 239 } 240 userName, host := parseUser(user.User) 241 sql := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE User="%s" AND Host="%s" AND DB="%s";`, mysql.SystemDB, mysql.DBTable, asgns, userName, host, db.Name.O) 242 _, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql) 243 return errors.Trace(err) 244 } 245 246 // Manipulate mysql.tables_priv table. 247 func (e *GrantExec) grantTablePriv(priv *ast.PrivElem, user *ast.UserSpec) error { 248 db, tbl, err := e.getTargetSchemaAndTable() 249 if err != nil { 250 return errors.Trace(err) 251 } 252 userName, host := parseUser(user.User) 253 asgns, err := composeTablePrivUpdate(e.ctx, priv.Priv, userName, host, db.Name.O, tbl.Meta().Name.O) 254 if err != nil { 255 return errors.Trace(err) 256 } 257 sql := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s";`, mysql.SystemDB, mysql.TablePrivTable, asgns, userName, host, db.Name.O, tbl.Meta().Name.O) 258 _, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql) 259 return errors.Trace(err) 260 } 261 262 // Manipulate mysql.tables_priv table. 263 func (e *GrantExec) grantColumnPriv(priv *ast.PrivElem, user *ast.UserSpec) error { 264 db, tbl, err := e.getTargetSchemaAndTable() 265 if err != nil { 266 return errors.Trace(err) 267 } 268 userName, host := parseUser(user.User) 269 for _, c := range priv.Cols { 270 col := column.FindCol(tbl.Cols(), c.Name.L) 271 if col == nil { 272 return errors.Errorf("Unknown column: %s", c) 273 } 274 asgns, err := composeColumnPrivUpdate(e.ctx, priv.Priv, userName, host, db.Name.O, tbl.Meta().Name.O, col.Name.O) 275 if err != nil { 276 return errors.Trace(err) 277 } 278 sql := fmt.Sprintf(`UPDATE %s.%s SET %s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s" AND Column_name="%s";`, mysql.SystemDB, mysql.ColumnPrivTable, asgns, userName, host, db.Name.O, tbl.Meta().Name.O, col.Name.O) 279 _, err = e.ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(e.ctx, sql) 280 if err != nil { 281 return errors.Trace(err) 282 } 283 } 284 return nil 285 } 286 287 // Compose update stmt assignment list string for global scope privilege update. 288 func composeGlobalPrivUpdate(priv mysql.PrivilegeType) (string, error) { 289 if priv == mysql.AllPriv { 290 strs := make([]string, 0, len(mysql.Priv2UserCol)) 291 for _, v := range mysql.Priv2UserCol { 292 strs = append(strs, fmt.Sprintf(`%s="Y"`, v)) 293 } 294 return strings.Join(strs, ", "), nil 295 } 296 col, ok := mysql.Priv2UserCol[priv] 297 if !ok { 298 return "", errors.Errorf("Unknown priv: %v", priv) 299 } 300 return fmt.Sprintf(`%s="Y"`, col), nil 301 } 302 303 // Compose update stmt assignment list for db scope privilege update. 304 func composeDBPrivUpdate(priv mysql.PrivilegeType) (string, error) { 305 if priv == mysql.AllPriv { 306 strs := make([]string, 0, len(mysql.AllDBPrivs)) 307 for _, p := range mysql.AllDBPrivs { 308 v, ok := mysql.Priv2UserCol[p] 309 if !ok { 310 return "", errors.Errorf("Unknown db privilege %v", priv) 311 } 312 strs = append(strs, fmt.Sprintf(`%s="Y"`, v)) 313 } 314 return strings.Join(strs, ", "), nil 315 } 316 col, ok := mysql.Priv2UserCol[priv] 317 if !ok { 318 return "", errors.Errorf("Unknown priv: %v", priv) 319 } 320 return fmt.Sprintf(`%s="Y"`, col), nil 321 } 322 323 // Compose update stmt assignment list for table scope privilege update. 324 func composeTablePrivUpdate(ctx context.Context, priv mysql.PrivilegeType, name string, host string, db string, tbl string) (string, error) { 325 var newTablePriv, newColumnPriv string 326 if priv == mysql.AllPriv { 327 for _, p := range mysql.AllTablePrivs { 328 v, ok := mysql.Priv2SetStr[p] 329 if !ok { 330 return "", errors.Errorf("Unknown table privilege %v", p) 331 } 332 if len(newTablePriv) == 0 { 333 newTablePriv = v 334 } else { 335 newTablePriv = fmt.Sprintf("%s,%s", newTablePriv, v) 336 } 337 } 338 for _, p := range mysql.AllColumnPrivs { 339 v, ok := mysql.Priv2SetStr[p] 340 if !ok { 341 return "", errors.Errorf("Unknown column privilege %v", p) 342 } 343 if len(newColumnPriv) == 0 { 344 newColumnPriv = v 345 } else { 346 newColumnPriv = fmt.Sprintf("%s,%s", newColumnPriv, v) 347 } 348 } 349 } else { 350 currTablePriv, currColumnPriv, err := getTablePriv(ctx, name, host, db, tbl) 351 if err != nil { 352 return "", errors.Trace(err) 353 } 354 p, ok := mysql.Priv2SetStr[priv] 355 if !ok { 356 return "", errors.Errorf("Unknown priv: %v", priv) 357 } 358 if len(currTablePriv) == 0 { 359 newTablePriv = p 360 } else { 361 newTablePriv = fmt.Sprintf("%s,%s", currTablePriv, p) 362 } 363 for _, cp := range mysql.AllColumnPrivs { 364 if priv == cp { 365 if len(currColumnPriv) == 0 { 366 newColumnPriv = p 367 } else { 368 newColumnPriv = fmt.Sprintf("%s,%s", currColumnPriv, p) 369 } 370 break 371 } 372 } 373 } 374 return fmt.Sprintf(`Table_priv="%s", Column_priv="%s", Grantor="%s"`, newTablePriv, newColumnPriv, variable.GetSessionVars(ctx).User), nil 375 } 376 377 // Compose update stmt assignment list for column scope privilege update. 378 func composeColumnPrivUpdate(ctx context.Context, priv mysql.PrivilegeType, name string, host string, db string, tbl string, col string) (string, error) { 379 newColumnPriv := "" 380 if priv == mysql.AllPriv { 381 for _, p := range mysql.AllColumnPrivs { 382 v, ok := mysql.Priv2SetStr[p] 383 if !ok { 384 return "", errors.Errorf("Unknown column privilege %v", p) 385 } 386 if len(newColumnPriv) == 0 { 387 newColumnPriv = v 388 } else { 389 newColumnPriv = fmt.Sprintf("%s,%s", newColumnPriv, v) 390 } 391 } 392 } else { 393 currColumnPriv, err := getColumnPriv(ctx, name, host, db, tbl, col) 394 if err != nil { 395 return "", errors.Trace(err) 396 } 397 p, ok := mysql.Priv2SetStr[priv] 398 if !ok { 399 return "", errors.Errorf("Unknown priv: %v", priv) 400 } 401 if len(currColumnPriv) == 0 { 402 newColumnPriv = p 403 } else { 404 newColumnPriv = fmt.Sprintf("%s,%s", currColumnPriv, p) 405 } 406 } 407 return fmt.Sprintf(`Column_priv="%s"`, newColumnPriv), nil 408 } 409 410 // Helper function to check if the sql returns any row. 411 func recordExists(ctx context.Context, sql string) (bool, error) { 412 rs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) 413 if err != nil { 414 return false, errors.Trace(err) 415 } 416 defer rs.Close() 417 row, err := rs.Next() 418 if err != nil { 419 return false, errors.Trace(err) 420 } 421 return row != nil, nil 422 } 423 424 // Check if there is an entry with key user-host-db in mysql.DB. 425 func dbUserExists(ctx context.Context, name string, host string, db string) (bool, error) { 426 sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s";`, mysql.SystemDB, mysql.DBTable, name, host, db) 427 return recordExists(ctx, sql) 428 } 429 430 // Check if there is an entry with key user-host-db-tbl in mysql.Tables_priv. 431 func tableUserExists(ctx context.Context, name string, host string, db string, tbl string) (bool, error) { 432 sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s";`, mysql.SystemDB, mysql.TablePrivTable, name, host, db, tbl) 433 return recordExists(ctx, sql) 434 } 435 436 // Check if there is an entry with key user-host-db-tbl-col in mysql.Columns_priv. 437 func columnPrivEntryExists(ctx context.Context, name string, host string, db string, tbl string, col string) (bool, error) { 438 sql := fmt.Sprintf(`SELECT * FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s" AND Column_name="%s";`, mysql.SystemDB, mysql.ColumnPrivTable, name, host, db, tbl, col) 439 return recordExists(ctx, sql) 440 } 441 442 // Get current table scope privilege set from mysql.Tables_priv. 443 // Return Table_priv and Column_priv. 444 func getTablePriv(ctx context.Context, name string, host string, db string, tbl string) (string, string, error) { 445 sql := fmt.Sprintf(`SELECT Table_priv, Column_priv FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s";`, mysql.SystemDB, mysql.TablePrivTable, name, host, db, tbl) 446 rs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) 447 if err != nil { 448 return "", "", errors.Trace(err) 449 } 450 defer rs.Close() 451 row, err := rs.Next() 452 if err != nil { 453 return "", "", errors.Trace(err) 454 } 455 var tPriv, cPriv string 456 if row.Data[0].Kind() == types.KindMysqlSet { 457 tablePriv := row.Data[0].GetMysqlSet() 458 tPriv = tablePriv.Name 459 } 460 if row.Data[1].Kind() == types.KindMysqlSet { 461 columnPriv := row.Data[1].GetMysqlSet() 462 cPriv = columnPriv.Name 463 } 464 return tPriv, cPriv, nil 465 } 466 467 // Get current column scope privilege set from mysql.Columns_priv. 468 // Return Column_priv. 469 func getColumnPriv(ctx context.Context, name string, host string, db string, tbl string, col string) (string, error) { 470 sql := fmt.Sprintf(`SELECT Column_priv FROM %s.%s WHERE User="%s" AND Host="%s" AND DB="%s" AND Table_name="%s" AND Column_name="%s";`, mysql.SystemDB, mysql.ColumnPrivTable, name, host, db, tbl, col) 471 rs, err := ctx.(sqlexec.RestrictedSQLExecutor).ExecRestrictedSQL(ctx, sql) 472 if err != nil { 473 return "", errors.Trace(err) 474 } 475 defer rs.Close() 476 row, err := rs.Next() 477 if err != nil { 478 return "", errors.Trace(err) 479 } 480 cPriv := "" 481 if row.Data[0].Kind() == types.KindMysqlSet { 482 cPriv = row.Data[0].GetMysqlSet().Name 483 } 484 return cPriv, nil 485 } 486 487 // Find the schema by dbName. 488 func (e *GrantExec) getTargetSchema() (*model.DBInfo, error) { 489 dbName := e.Level.DBName 490 if len(dbName) == 0 { 491 // Grant *, use current schema 492 dbName = db.GetCurrentSchema(e.ctx) 493 if len(dbName) == 0 { 494 return nil, errors.New("Miss DB name for grant privilege.") 495 } 496 } 497 //check if db exists 498 schema := model.NewCIStr(dbName) 499 is := sessionctx.GetDomain(e.ctx).InfoSchema() 500 db, ok := is.SchemaByName(schema) 501 if !ok { 502 return nil, errors.Errorf("Unknown schema name: %s", dbName) 503 } 504 return db, nil 505 } 506 507 // Find the schema and table by dbName and tableName. 508 func (e *GrantExec) getTargetSchemaAndTable() (*model.DBInfo, table.Table, error) { 509 db, err := e.getTargetSchema() 510 if err != nil { 511 return nil, nil, errors.Trace(err) 512 } 513 name := model.NewCIStr(e.Level.TableName) 514 is := sessionctx.GetDomain(e.ctx).InfoSchema() 515 tbl, err := is.TableByName(db.Name, name) 516 if err != nil { 517 return nil, nil, errors.Trace(err) 518 } 519 return db, tbl, nil 520 }