github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/sql/alter_role.go (about) 1 // Copyright 2017 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 "fmt" 16 17 "github.com/cockroachdb/cockroach/pkg/clusterversion" 18 "github.com/cockroachdb/cockroach/pkg/security" 19 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgcode" 20 "github.com/cockroachdb/cockroach/pkg/sql/pgwire/pgerror" 21 "github.com/cockroachdb/cockroach/pkg/sql/roleoption" 22 "github.com/cockroachdb/cockroach/pkg/sql/sem/tree" 23 "github.com/cockroachdb/cockroach/pkg/sql/sqlbase" 24 "github.com/cockroachdb/cockroach/pkg/sql/sqltelemetry" 25 "github.com/cockroachdb/errors" 26 ) 27 28 // AlterRoleNode represents an ALTER ROLE ... [WITH] OPTION... statement. 29 type alterRoleNode struct { 30 userNameInfo 31 ifExists bool 32 isRole bool 33 roleOptions roleoption.List 34 } 35 36 // AlterRole represents a ALTER ROLE statement. 37 // Privileges: CREATEROLE privilege. 38 func (p *planner) AlterRole(ctx context.Context, n *tree.AlterRole) (planNode, error) { 39 return p.AlterRoleNode(ctx, n.Name, n.IfExists, n.IsRole, "ALTER ROLE", n.KVOptions) 40 } 41 42 func (p *planner) AlterRoleNode( 43 ctx context.Context, 44 nameE tree.Expr, 45 ifExists bool, 46 isRole bool, 47 opName string, 48 kvOptions tree.KVOptions, 49 ) (*alterRoleNode, error) { 50 // Note that for Postgres, only superuser can ALTER another superuser. 51 // CockroachDB does not support superuser privilege right now. 52 // However we make it so the admin role cannot be edited (done in startExec). 53 if err := p.HasRoleOption(ctx, roleoption.CREATEROLE); err != nil { 54 return nil, err 55 } 56 57 asStringOrNull := func(e tree.Expr, op string) (func() (bool, string, error), error) { 58 return p.TypeAsStringOrNull(ctx, e, op) 59 } 60 roleOptions, err := kvOptions.ToRoleOptions(asStringOrNull, opName) 61 if err != nil { 62 return nil, err 63 } 64 if err := roleOptions.CheckRoleOptionConflicts(); err != nil { 65 return nil, err 66 } 67 68 ua, err := p.getUserAuthInfo(ctx, nameE, opName) 69 if err != nil { 70 return nil, err 71 } 72 73 return &alterRoleNode{ 74 userNameInfo: ua, 75 ifExists: ifExists, 76 isRole: isRole, 77 roleOptions: roleOptions, 78 }, nil 79 } 80 81 func (n *alterRoleNode) startExec(params runParams) error { 82 var opName string 83 if n.isRole { 84 sqltelemetry.IncIAMAlterCounter(sqltelemetry.Role) 85 opName = "alter-role" 86 } else { 87 sqltelemetry.IncIAMAlterCounter(sqltelemetry.User) 88 opName = "alter-user" 89 } 90 name, err := n.name() 91 if err != nil { 92 return err 93 } 94 if name == "" { 95 return errNoUserNameSpecified 96 } 97 if name == "admin" { 98 return pgerror.Newf(pgcode.InsufficientPrivilege, 99 "cannot edit admin role") 100 } 101 normalizedUsername, err := NormalizeAndValidateUsername(name) 102 if err != nil { 103 return err 104 } 105 106 // Check if role exists. 107 row, err := params.extendedEvalCtx.ExecCfg.InternalExecutor.QueryRowEx( 108 params.ctx, 109 opName, 110 params.p.txn, 111 sqlbase.InternalExecutorSessionDataOverride{User: security.RootUser}, 112 fmt.Sprintf("SELECT 1 FROM %s WHERE username = $1", userTableName), 113 normalizedUsername, 114 ) 115 if err != nil { 116 return err 117 } 118 if row == nil { 119 if n.ifExists { 120 return nil 121 } 122 return errors.Newf("role/user %s does not exist", normalizedUsername) 123 } 124 125 if n.roleOptions.Contains(roleoption.PASSWORD) { 126 hashedPassword, err := n.roleOptions.GetHashedPassword() 127 if err != nil { 128 return err 129 } 130 131 // TODO(knz): Remove in 20.2. 132 if normalizedUsername == security.RootUser && len(hashedPassword) > 0 && 133 !params.EvalContext().Settings.Version.IsActive(params.ctx, clusterversion.VersionRootPassword) { 134 return pgerror.Newf(pgcode.ObjectNotInPrerequisiteState, 135 `setting a root password requires all nodes to be upgraded to %s`, 136 clusterversion.VersionByKey(clusterversion.VersionRootPassword), 137 ) 138 } 139 140 if len(hashedPassword) > 0 && params.extendedEvalCtx.ExecCfg.RPCContext.Insecure { 141 // We disallow setting a non-empty password in insecure mode 142 // because insecure means an observer may have MITM'ed the change 143 // and learned the password. 144 // 145 // It's valid to clear the password (WITH PASSWORD NULL) however 146 // since that forces cert auth when moving back to secure mode, 147 // and certs can't be MITM'ed over the insecure SQL connection. 148 return pgerror.New(pgcode.InvalidPassword, 149 "setting or updating a password is not supported in insecure mode") 150 } 151 152 if hashedPassword == nil { 153 // v20.1 and below crash during authentication if they find a NULL value 154 // in system.users.hashedPassword. v20.2 and above handle this correctly, 155 // but we need to maintain mixed version compatibility for at least one 156 // release. 157 // TODO(nvanbenschoten): remove this for v21.1. 158 hashedPassword = []byte{} 159 } 160 161 // Updating PASSWORD is a special case since PASSWORD lives in system.users 162 // while the rest of the role options lives in system.role_options. 163 _, err = params.extendedEvalCtx.ExecCfg.InternalExecutor.Exec( 164 params.ctx, 165 opName, 166 params.p.txn, 167 `UPDATE system.users SET "hashedPassword" = $2 WHERE username = $1`, 168 normalizedUsername, 169 hashedPassword, 170 ) 171 if err != nil { 172 return err 173 } 174 } 175 176 // Get a map of statements to execute for role options and their values. 177 stmts, err := n.roleOptions.GetSQLStmts(sqltelemetry.AlterRole) 178 if err != nil { 179 return err 180 } 181 182 for stmt, value := range stmts { 183 qargs := []interface{}{normalizedUsername} 184 185 if value != nil { 186 isNull, val, err := value() 187 if err != nil { 188 return err 189 } 190 if isNull { 191 // If the value of the role option is NULL, ensure that nil is passed 192 // into the statement placeholder, since val is string type "NULL" 193 // will not be interpreted as NULL by the InternalExecutor. 194 qargs = append(qargs, nil) 195 } else { 196 qargs = append(qargs, val) 197 } 198 } 199 200 _, err := params.extendedEvalCtx.ExecCfg.InternalExecutor.ExecEx( 201 params.ctx, 202 opName, 203 params.p.txn, 204 sqlbase.InternalExecutorSessionDataOverride{User: security.RootUser}, 205 stmt, 206 qargs..., 207 ) 208 if err != nil { 209 return err 210 } 211 } 212 213 return nil 214 } 215 216 func (*alterRoleNode) Next(runParams) (bool, error) { return false, nil } 217 func (*alterRoleNode) Values() tree.Datums { return tree.Datums{} } 218 func (*alterRoleNode) Close(context.Context) {}