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) {}