github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/revoke.go (about)

     1  // Copyright 2020 WHTCORPS INC, 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 interlock
    15  
    16  import (
    17  	"context"
    18  	"fmt"
    19  
    20  	"github.com/whtcorpsinc/errors"
    21  	"github.com/whtcorpsinc/BerolinaSQL/ast"
    22  	"github.com/whtcorpsinc/BerolinaSQL/allegrosql"
    23  	"github.com/whtcorpsinc/milevadb/petri"
    24  	"github.com/whtcorpsinc/milevadb/schemareplicant"
    25  	"github.com/whtcorpsinc/milevadb/stochastikctx"
    26  	"github.com/whtcorpsinc/milevadb/causet"
    27  	"github.com/whtcorpsinc/milevadb/soliton/chunk"
    28  	"github.com/whtcorpsinc/milevadb/soliton/logutil"
    29  	"github.com/whtcorpsinc/milevadb/soliton/sqlexec"
    30  	"go.uber.org/zap"
    31  )
    32  
    33  /***
    34   * Revoke Statement
    35   * See https://dev.allegrosql.com/doc/refman/5.7/en/revoke.html
    36   ************************************************************************************/
    37  var (
    38  	_ InterlockingDirectorate = (*RevokeInterDirc)(nil)
    39  )
    40  
    41  // RevokeInterDirc executes RevokeStmt.
    42  type RevokeInterDirc struct {
    43  	baseInterlockingDirectorate
    44  
    45  	Privs      []*ast.PrivElem
    46  	ObjectType ast.ObjectTypeType
    47  	Level      *ast.GrantLevel
    48  	Users      []*ast.UserSpec
    49  
    50  	ctx  stochastikctx.Context
    51  	is   schemareplicant.SchemaReplicant
    52  	done bool
    53  }
    54  
    55  // Next implements the InterlockingDirectorate Next interface.
    56  func (e *RevokeInterDirc) Next(ctx context.Context, req *chunk.Chunk) error {
    57  	if e.done {
    58  		return nil
    59  	}
    60  	e.done = true
    61  
    62  	// Commit the old transaction, like DBS.
    63  	if err := e.ctx.NewTxn(ctx); err != nil {
    64  		return err
    65  	}
    66  	defer func() { e.ctx.GetStochastikVars().SetStatusFlag(allegrosql.ServerStatusInTrans, false) }()
    67  
    68  	// Create internal stochastik to start internal transaction.
    69  	isCommit := false
    70  	internalStochastik, err := e.getSysStochastik()
    71  	if err != nil {
    72  		return err
    73  	}
    74  	defer func() {
    75  		if !isCommit {
    76  			_, err := internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), "rollback")
    77  			if err != nil {
    78  				logutil.BgLogger().Error("rollback error occur at grant privilege", zap.Error(err))
    79  			}
    80  		}
    81  		e.releaseSysStochastik(internalStochastik)
    82  	}()
    83  
    84  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), "begin")
    85  	if err != nil {
    86  		return err
    87  	}
    88  
    89  	// Revoke for each user.
    90  	for _, user := range e.Users {
    91  		// Check if user exists.
    92  		exists, err := userExists(e.ctx, user.User.Username, user.User.Hostname)
    93  		if err != nil {
    94  			return err
    95  		}
    96  		if !exists {
    97  			return errors.Errorf("Unknown user: %s", user.User)
    98  		}
    99  
   100  		err = e.revokeOneUser(internalStochastik, user.User.Username, user.User.Hostname)
   101  		if err != nil {
   102  			return err
   103  		}
   104  	}
   105  
   106  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), "commit")
   107  	if err != nil {
   108  		return err
   109  	}
   110  	isCommit = true
   111  	petri.GetPetri(e.ctx).NotifyUFIDelatePrivilege(e.ctx)
   112  	return nil
   113  }
   114  
   115  func (e *RevokeInterDirc) revokeOneUser(internalStochastik stochastikctx.Context, user, host string) error {
   116  	dbName := e.Level.DBName
   117  	if len(dbName) == 0 {
   118  		dbName = e.ctx.GetStochastikVars().CurrentDB
   119  	}
   120  
   121  	// If there is no privilege entry in corresponding causet, insert a new one.
   122  	// EDB scope:		allegrosql.EDB
   123  	// Block scope:		allegrosql.Blocks_priv
   124  	// DeferredCauset scope:	allegrosql.DeferredCausets_priv
   125  	switch e.Level.Level {
   126  	case ast.GrantLevelDB:
   127  		ok, err := dbUserExists(internalStochastik, user, host, dbName)
   128  		if err != nil {
   129  			return err
   130  		}
   131  		if !ok {
   132  			return errors.Errorf("There is no such grant defined for user '%s' on host '%s' on database %s", user, host, dbName)
   133  		}
   134  	case ast.GrantLevelBlock:
   135  		ok, err := blockUserExists(internalStochastik, user, host, dbName, e.Level.BlockName)
   136  		if err != nil {
   137  			return err
   138  		}
   139  		if !ok {
   140  			return errors.Errorf("There is no such grant defined for user '%s' on host '%s' on causet %s.%s", user, host, dbName, e.Level.BlockName)
   141  		}
   142  	}
   143  
   144  	for _, priv := range e.Privs {
   145  		err := e.revokePriv(internalStochastik, priv, user, host)
   146  		if err != nil {
   147  			return err
   148  		}
   149  	}
   150  	return nil
   151  }
   152  
   153  func (e *RevokeInterDirc) revokePriv(internalStochastik stochastikctx.Context, priv *ast.PrivElem, user, host string) error {
   154  	switch e.Level.Level {
   155  	case ast.GrantLevelGlobal:
   156  		return e.revokeGlobalPriv(internalStochastik, priv, user, host)
   157  	case ast.GrantLevelDB:
   158  		return e.revokeDBPriv(internalStochastik, priv, user, host)
   159  	case ast.GrantLevelBlock:
   160  		if len(priv.DefCauss) == 0 {
   161  			return e.revokeBlockPriv(internalStochastik, priv, user, host)
   162  		}
   163  		return e.revokeDeferredCausetPriv(internalStochastik, priv, user, host)
   164  	}
   165  	return errors.Errorf("Unknown revoke level: %#v", e.Level)
   166  }
   167  
   168  func (e *RevokeInterDirc) revokeGlobalPriv(internalStochastik stochastikctx.Context, priv *ast.PrivElem, user, host string) error {
   169  	asgns, err := composeGlobalPrivUFIDelate(priv.Priv, "N")
   170  	if err != nil {
   171  		return err
   172  	}
   173  	allegrosql := fmt.Sprintf(`UFIDelATE %s.%s SET %s WHERE User='%s' AND Host='%s'`, allegrosql.SystemDB, allegrosql.UserBlock, asgns, user, host)
   174  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   175  	return err
   176  }
   177  
   178  func (e *RevokeInterDirc) revokeDBPriv(internalStochastik stochastikctx.Context, priv *ast.PrivElem, userName, host string) error {
   179  	dbName := e.Level.DBName
   180  	if len(dbName) == 0 {
   181  		dbName = e.ctx.GetStochastikVars().CurrentDB
   182  	}
   183  	asgns, err := composeDBPrivUFIDelate(priv.Priv, "N")
   184  	if err != nil {
   185  		return err
   186  	}
   187  	allegrosql := fmt.Sprintf(`UFIDelATE %s.%s SET %s WHERE User='%s' AND Host='%s' AND EDB='%s';`, allegrosql.SystemDB, allegrosql.DBBlock, asgns, userName, host, dbName)
   188  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   189  	return err
   190  }
   191  
   192  func (e *RevokeInterDirc) revokeBlockPriv(internalStochastik stochastikctx.Context, priv *ast.PrivElem, user, host string) error {
   193  	dbName, tbl, err := getTargetSchemaAndBlock(e.ctx, e.Level.DBName, e.Level.BlockName, e.is)
   194  	if err != nil {
   195  		return err
   196  	}
   197  	asgns, err := composeBlockPrivUFIDelateForRevoke(internalStochastik, priv.Priv, user, host, dbName, tbl.Meta().Name.O)
   198  	if err != nil {
   199  		return err
   200  	}
   201  	allegrosql := fmt.Sprintf(`UFIDelATE %s.%s SET %s WHERE User='%s' AND Host='%s' AND EDB='%s' AND Block_name='%s';`, allegrosql.SystemDB, allegrosql.BlockPrivBlock, asgns, user, host, dbName, tbl.Meta().Name.O)
   202  	_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   203  	return err
   204  }
   205  
   206  func (e *RevokeInterDirc) revokeDeferredCausetPriv(internalStochastik stochastikctx.Context, priv *ast.PrivElem, user, host string) error {
   207  	dbName, tbl, err := getTargetSchemaAndBlock(e.ctx, e.Level.DBName, e.Level.BlockName, e.is)
   208  	if err != nil {
   209  		return err
   210  	}
   211  	for _, c := range priv.DefCauss {
   212  		defCaus := causet.FindDefCaus(tbl.DefCauss(), c.Name.L)
   213  		if defCaus == nil {
   214  			return errors.Errorf("Unknown defCausumn: %s", c)
   215  		}
   216  		asgns, err := composeDeferredCausetPrivUFIDelateForRevoke(internalStochastik, priv.Priv, user, host, dbName, tbl.Meta().Name.O, defCaus.Name.O)
   217  		if err != nil {
   218  			return err
   219  		}
   220  		allegrosql := fmt.Sprintf(`UFIDelATE %s.%s SET %s WHERE User='%s' AND Host='%s' AND EDB='%s' AND Block_name='%s' AND DeferredCauset_name='%s';`, allegrosql.SystemDB, allegrosql.DeferredCausetPrivBlock, asgns, user, host, dbName, tbl.Meta().Name.O, defCaus.Name.O)
   221  		_, err = internalStochastik.(sqlexec.ALLEGROSQLInterlockingDirectorate).InterDircute(context.Background(), allegrosql)
   222  		if err != nil {
   223  			return err
   224  		}
   225  	}
   226  	return nil
   227  }