github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/grant_revoke.go (about)

     1  // Copyright 2015 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package sql
    12  
    13  import (
    14  	"context"
    15  
    16  	"github.com/cockroachdb/cockroach/pkg/sql/catalog/catalogkv"
    17  	"github.com/cockroachdb/cockroach/pkg/sql/privilege"
    18  	"github.com/cockroachdb/cockroach/pkg/sql/sem/tree"
    19  	"github.com/cockroachdb/cockroach/pkg/sql/sqlbase"
    20  	"github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry"
    21  	"github.com/cockroachdb/errors"
    22  )
    23  
    24  // Grant adds privileges to users.
    25  // Current status:
    26  // - Target: single database, table, or view.
    27  // TODO(marc): open questions:
    28  // - should we have root always allowed and not present in the permissions list?
    29  // - should we make users case-insensitive?
    30  // Privileges: GRANT on database/table/view.
    31  //   Notes: postgres requires the object owner.
    32  //          mysql requires the "grant option" and the same privileges, and sometimes superuser.
    33  func (p *planner) Grant(ctx context.Context, n *tree.Grant) (planNode, error) {
    34  	if n.Targets.Databases != nil {
    35  		sqltelemetry.IncIAMGrantPrivilegesCounter(sqltelemetry.OnDatabase)
    36  	} else {
    37  		sqltelemetry.IncIAMGrantPrivilegesCounter(sqltelemetry.OnTable)
    38  	}
    39  
    40  	return &changePrivilegesNode{
    41  		targets:      n.Targets,
    42  		grantees:     n.Grantees,
    43  		desiredprivs: n.Privileges,
    44  		changePrivilege: func(privDesc *sqlbase.PrivilegeDescriptor, grantee string) {
    45  			privDesc.Grant(grantee, n.Privileges)
    46  		},
    47  	}, nil
    48  }
    49  
    50  // Revoke removes privileges from users.
    51  // Current status:
    52  // - Target: single database, table, or view.
    53  // TODO(marc): open questions:
    54  // - should we have root always allowed and not present in the permissions list?
    55  // - should we make users case-insensitive?
    56  // Privileges: GRANT on database/table/view.
    57  //   Notes: postgres requires the object owner.
    58  //          mysql requires the "grant option" and the same privileges, and sometimes superuser.
    59  func (p *planner) Revoke(ctx context.Context, n *tree.Revoke) (planNode, error) {
    60  	if n.Targets.Databases != nil {
    61  		sqltelemetry.IncIAMRevokePrivilegesCounter(sqltelemetry.OnDatabase)
    62  	} else {
    63  		sqltelemetry.IncIAMRevokePrivilegesCounter(sqltelemetry.OnTable)
    64  	}
    65  
    66  	return &changePrivilegesNode{
    67  		targets:      n.Targets,
    68  		grantees:     n.Grantees,
    69  		desiredprivs: n.Privileges,
    70  		changePrivilege: func(privDesc *sqlbase.PrivilegeDescriptor, grantee string) {
    71  			privDesc.Revoke(grantee, n.Privileges)
    72  		},
    73  	}, nil
    74  }
    75  
    76  type changePrivilegesNode struct {
    77  	targets         tree.TargetList
    78  	grantees        tree.NameList
    79  	desiredprivs    privilege.List
    80  	changePrivilege func(*sqlbase.PrivilegeDescriptor, string)
    81  }
    82  
    83  // ReadingOwnWrites implements the planNodeReadingOwnWrites interface.
    84  // This is because GRANT/REVOKE performs multiple KV operations on descriptors
    85  // and expects to see its own writes.
    86  func (n *changePrivilegesNode) ReadingOwnWrites() {}
    87  
    88  func (n *changePrivilegesNode) startExec(params runParams) error {
    89  	ctx := params.ctx
    90  	p := params.p
    91  	// Check whether grantees exists
    92  	users, err := p.GetAllRoles(ctx)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	// We're allowed to grant/revoke privileges to/from the "public" role even though
    98  	// it does not exist: add it to the list of all users and roles.
    99  	users[sqlbase.PublicRole] = true // isRole
   100  
   101  	for _, grantee := range n.grantees {
   102  		if _, ok := users[string(grantee)]; !ok {
   103  			return errors.Errorf("user or role %s does not exist", &grantee)
   104  		}
   105  	}
   106  
   107  	var descriptors []sqlbase.DescriptorProto
   108  	// DDL statements avoid the cache to avoid leases, and can view non-public descriptors.
   109  	// TODO(vivek): check if the cache can be used.
   110  	p.runWithOptions(resolveFlags{skipCache: true}, func() {
   111  		descriptors, err = getDescriptorsFromTargetList(ctx, p, n.targets)
   112  	})
   113  	if err != nil {
   114  		return err
   115  	}
   116  
   117  	// First, update the descriptors. We want to catch all errors before
   118  	// we update them in KV below.
   119  	b := p.txn.NewBatch()
   120  	for _, descriptor := range descriptors {
   121  		if err := p.CheckPrivilege(ctx, descriptor, privilege.GRANT); err != nil {
   122  			return err
   123  		}
   124  
   125  		// Only allow granting/revoking privileges that the requesting
   126  		// user themselves have on the descriptor.
   127  		for _, priv := range n.desiredprivs {
   128  			if err := p.CheckPrivilege(ctx, descriptor, priv); err != nil {
   129  				return err
   130  			}
   131  		}
   132  
   133  		privileges := descriptor.GetPrivileges()
   134  		for _, grantee := range n.grantees {
   135  			n.changePrivilege(privileges, string(grantee))
   136  		}
   137  
   138  		// Validate privilege descriptors directly as the db/table level Validate
   139  		// may fix up the descriptor.
   140  		if err := privileges.Validate(descriptor.GetID()); err != nil {
   141  			return err
   142  		}
   143  
   144  		switch d := descriptor.(type) {
   145  		case *sqlbase.DatabaseDescriptor:
   146  			if err := d.Validate(); err != nil {
   147  				return err
   148  			}
   149  			if err := catalogkv.WriteDescToBatch(
   150  				ctx,
   151  				p.extendedEvalCtx.Tracing.KVTracingEnabled(),
   152  				p.ExecCfg().Settings,
   153  				b,
   154  				p.ExecCfg().Codec,
   155  				descriptor.GetID(),
   156  				descriptor,
   157  			); err != nil {
   158  				return err
   159  			}
   160  
   161  		case *sqlbase.MutableTableDescriptor:
   162  			// TODO (lucy): This should probably have a single consolidated job like
   163  			// DROP DATABASE.
   164  			// TODO (lucy): Have more consistent/informative names for dependent jobs.
   165  			if err := p.createOrUpdateSchemaChangeJob(
   166  				ctx, d, "updating privileges", sqlbase.InvalidMutationID,
   167  			); err != nil {
   168  				return err
   169  			}
   170  			if !d.Dropped() {
   171  				if err := p.writeSchemaChangeToBatch(ctx, d, b); err != nil {
   172  					return err
   173  				}
   174  			}
   175  		}
   176  	}
   177  
   178  	// Now update the descriptors transactionally.
   179  	return p.txn.Run(ctx, b)
   180  }
   181  
   182  func (*changePrivilegesNode) Next(runParams) (bool, error) { return false, nil }
   183  func (*changePrivilegesNode) Values() tree.Datums          { return tree.Datums{} }
   184  func (*changePrivilegesNode) Close(context.Context)        {}