diff --git a/docs/generated/settings/settings.html b/docs/generated/settings/settings.html index 7da7383631ca..b1b254dffd8e 100644 --- a/docs/generated/settings/settings.html +++ b/docs/generated/settings/settings.html @@ -71,6 +71,6 @@ trace.debug.enablebooleanfalseif set, traces for recent requests can be seen in the /debug page trace.lightstep.tokenstringif set, traces go to Lightstep using this token trace.zipkin.collectorstringif set, traces go to the given Zipkin instance (example: '127.0.0.1:9411'); ignored if trace.lightstep.token is set -versioncustom validation20.1-7set the active cluster version in the format '.' +versioncustom validation20.1-8set the active cluster version in the format '.' diff --git a/docs/generated/sql/bnf/stmt_block.bnf b/docs/generated/sql/bnf/stmt_block.bnf index 201a7b74e2d1..2fc44e5d88d6 100644 --- a/docs/generated/sql/bnf/stmt_block.bnf +++ b/docs/generated/sql/bnf/stmt_block.bnf @@ -760,6 +760,7 @@ unreserved_keyword ::= | 'NO_INDEX_JOIN' | 'NOCREATEROLE' | 'NOLOGIN' + | 'NOSETPASSWORD' | 'NOWAIT' | 'NULLS' | 'IGNORE_FOREIGN_KEYS' @@ -813,6 +814,7 @@ unreserved_keyword ::= | 'ROLLUP' | 'ROWS' | 'RULE' + | 'SETPASSWORD' | 'SETTING' | 'SETTINGS' | 'STATUS' @@ -1805,6 +1807,8 @@ role_option ::= | 'NOCREATEROLE' | 'LOGIN' | 'NOLOGIN' + | 'SETPASSWORD' + | 'NOSETPASSWORD' | password_clause | valid_until_clause diff --git a/pkg/clusterversion/cockroach_versions.go b/pkg/clusterversion/cockroach_versions.go index 35d339a5dd42..3beea06ca97d 100644 --- a/pkg/clusterversion/cockroach_versions.go +++ b/pkg/clusterversion/cockroach_versions.go @@ -67,6 +67,7 @@ const ( VersionAlterColumnTypeGeneral VersionAlterSystemJobsAddCreatedByColumns VersionAddScheduledJobsTable + VersionSetPasswordPrivilege // Add new versions here (step one of two). ) @@ -511,6 +512,13 @@ var versionsSingleton = keyedVersions([]keyedVersion{ Key: VersionAddScheduledJobsTable, Version: roachpb.Version{Major: 20, Minor: 1, Unstable: 7}, }, + { + // VersionSetPasswordPrivilege is when SETPASSWORD/NOSETPASSWORD are introduced. + // + // It represents adding password management via SETPASSWORD role option. + Key: VersionSetPasswordPrivilege, + Version: roachpb.Version{Major: 20, Minor: 1, Unstable: 8}, + }, // Add new versions here (step two of two). diff --git a/pkg/clusterversion/versionkey_string.go b/pkg/clusterversion/versionkey_string.go index 76675efee0be..4040b51fb325 100644 --- a/pkg/clusterversion/versionkey_string.go +++ b/pkg/clusterversion/versionkey_string.go @@ -43,11 +43,12 @@ func _() { _ = x[VersionAlterColumnTypeGeneral-32] _ = x[VersionAlterSystemJobsAddCreatedByColumns-33] _ = x[VersionAddScheduledJobsTable-34] + _ = x[VersionSetPasswordPrivilege-35] } -const _VersionKey_name = "Version19_1VersionStart19_2VersionLearnerReplicasVersionTopLevelForeignKeysVersionAtomicChangeReplicasTriggerVersionAtomicChangeReplicasVersionTableDescModificationTimeFromMVCCVersionPartitionedBackupVersion19_2VersionStart20_1VersionContainsEstimatesCounterVersionChangeReplicasDemotionVersionSecondaryIndexColumnFamiliesVersionNamespaceTableWithSchemasVersionProtectedTimestampsVersionPrimaryKeyChangesVersionAuthLocalAndTrustRejectMethodsVersionPrimaryKeyColumnsOutOfFamilyZeroVersionRootPasswordVersionNoExplicitForeignKeyIndexIDsVersionHashShardedIndexesVersionCreateRolePrivilegeVersionStatementDiagnosticsSystemTablesVersionSchemaChangeJobVersionSavepointsVersionTimeTZTypeVersionTimePrecisionVersion20_1VersionStart20_2VersionGeospatialTypeVersionEnumsVersionRangefeedLeasesVersionAlterColumnTypeGeneralVersionAlterSystemJobsAddCreatedByColumnsVersionAddScheduledJobsTable" +const _VersionKey_name = "Version19_1VersionStart19_2VersionLearnerReplicasVersionTopLevelForeignKeysVersionAtomicChangeReplicasTriggerVersionAtomicChangeReplicasVersionTableDescModificationTimeFromMVCCVersionPartitionedBackupVersion19_2VersionStart20_1VersionContainsEstimatesCounterVersionChangeReplicasDemotionVersionSecondaryIndexColumnFamiliesVersionNamespaceTableWithSchemasVersionProtectedTimestampsVersionPrimaryKeyChangesVersionAuthLocalAndTrustRejectMethodsVersionPrimaryKeyColumnsOutOfFamilyZeroVersionRootPasswordVersionNoExplicitForeignKeyIndexIDsVersionHashShardedIndexesVersionCreateRolePrivilegeVersionStatementDiagnosticsSystemTablesVersionSchemaChangeJobVersionSavepointsVersionTimeTZTypeVersionTimePrecisionVersion20_1VersionStart20_2VersionGeospatialTypeVersionEnumsVersionRangefeedLeasesVersionAlterColumnTypeGeneralVersionAlterSystemJobsAddCreatedByColumnsVersionAddScheduledJobsTableVersionSetPasswordPrivilege" -var _VersionKey_index = [...]uint16{0, 11, 27, 49, 75, 109, 136, 176, 200, 211, 227, 258, 287, 322, 354, 380, 404, 441, 480, 499, 534, 559, 585, 624, 646, 663, 680, 700, 711, 727, 748, 760, 782, 811, 852, 880} +var _VersionKey_index = [...]uint16{0, 11, 27, 49, 75, 109, 136, 176, 200, 211, 227, 258, 287, 322, 354, 380, 404, 441, 480, 499, 534, 559, 585, 624, 646, 663, 680, 700, 711, 727, 748, 760, 782, 811, 852, 880, 907} func (i VersionKey) String() string { if i < 0 || i >= VersionKey(len(_VersionKey_index)-1) { diff --git a/pkg/sql/alter_role.go b/pkg/sql/alter_role.go index 619d51294bff..4c9e8776a309 100644 --- a/pkg/sql/alter_role.go +++ b/pkg/sql/alter_role.go @@ -65,6 +65,13 @@ func (p *planner) AlterRoleNode( return nil, err } + // Check that the requested combination of + // PASSWORD/SETPASSWORD/NOSETPASSWORD is compatible with the user's + // own SETPASSWORD privilege. + if err := p.checkPasswordOptionConstraints(ctx, roleOptions); err != nil { + return nil, err + } + ua, err := p.getUserAuthInfo(ctx, nameE, opName) if err != nil { return nil, err @@ -78,6 +85,33 @@ func (p *planner) AlterRoleNode( }, nil } +func (p *planner) checkPasswordOptionConstraints( + ctx context.Context, roleOptions roleoption.List, +) error { + if !p.EvalContext().Settings.Version.IsActive(ctx, clusterversion.VersionSetPasswordPrivilege) { + // TODO(knz): Remove this condition in 21.1. + if roleOptions.Contains(roleoption.SETPASSWORD) || roleOptions.Contains(roleoption.NOSETPASSWORD) { + return pgerror.Newf(pgcode.ObjectNotInPrerequisiteState, + `granting SETPASSWORD or NOSETPASSWORD requires all nodes to be upgraded to %s`, + clusterversion.VersionByKey(clusterversion.VersionSetPasswordPrivilege)) + } + } else { + if roleOptions.Contains(roleoption.SETPASSWORD) || + roleOptions.Contains(roleoption.NOSETPASSWORD) || + roleOptions.Contains(roleoption.PASSWORD) || + roleOptions.Contains(roleoption.VALIDUNTIL) { + // Only a role who has SETPASSWORD itself can grant SETPASSWORD + // or NOSETPASSWORD to another role, or set up a password for + // authentication, or set up password validity, even if they + // have CREATEROLE privilege. + if err := p.HasRoleOption(ctx, roleoption.SETPASSWORD); err != nil { + return err + } + } + } + return nil +} + func (n *alterRoleNode) startExec(params runParams) error { var opName string if n.isRole { diff --git a/pkg/sql/create_role.go b/pkg/sql/create_role.go index 3d4a5ba89f09..a5799d670a97 100644 --- a/pkg/sql/create_role.go +++ b/pkg/sql/create_role.go @@ -66,6 +66,9 @@ func (p *planner) CreateRoleNode( return p.TypeAsStringOrNull(ctx, e, op) } roleOptions, err := kvOptions.ToRoleOptions(asStringOrNull, opName) + if err != nil { + return nil, err + } // Using CREATE ROLE syntax enables NOLOGIN by default. if isRole && !roleOptions.Contains(roleoption.LOGIN) && @@ -74,11 +77,14 @@ func (p *planner) CreateRoleNode( roleoption.RoleOption{Option: roleoption.NOLOGIN, HasValue: false}) } - if err != nil { + if err := roleOptions.CheckRoleOptionConflicts(); err != nil { return nil, err } - if err := roleOptions.CheckRoleOptionConflicts(); err != nil { + // Check that the requested combination of + // PASSWORD/SETPASSWORD/NOSETPASSWORD is compatible with the user's + // own SETPASSWORD privilege. + if err := p.checkPasswordOptionConstraints(ctx, roleOptions); err != nil { return nil, err } @@ -112,6 +118,10 @@ func (n *CreateRoleNode) startExec(params runParams) error { var hashedPassword []byte if n.roleOptions.Contains(roleoption.PASSWORD) { + if err := params.p.HasRoleOption(params.ctx, roleoption.SETPASSWORD); err != nil { + return err + } + hashedPassword, err = n.roleOptions.GetHashedPassword() if err != nil { return err diff --git a/pkg/sql/logictest/testdata/logic_test/drop_user b/pkg/sql/logictest/testdata/logic_test/drop_user index 298ab91fefe9..7a38d1eacfdc 100644 --- a/pkg/sql/logictest/testdata/logic_test/drop_user +++ b/pkg/sql/logictest/testdata/logic_test/drop_user @@ -6,11 +6,11 @@ CREATE USER user1 query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} -user1 · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} +user1 · {} statement ok DROP USER user1 @@ -18,10 +18,10 @@ DROP USER user1 query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} statement ok CREATE USER user1 @@ -29,11 +29,11 @@ CREATE USER user1 query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} -user1 · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} +user1 · {} statement ok DROP USER USEr1 @@ -41,10 +41,10 @@ DROP USER USEr1 query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} statement error user user1 does not exist DROP USER user1 @@ -76,14 +76,14 @@ CREATE USER user4 query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} -user1 · {} -user2 · {} -user3 · {} -user4 · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} +user1 · {} +user2 · {} +user3 · {} +user4 · {} statement ok DROP USER user1,user2 @@ -91,12 +91,12 @@ DROP USER user1,user2 query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} -user3 · {} -user4 · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} +user3 · {} +user4 · {} statement error user user1 does not exist DROP USER user1,user3 @@ -104,12 +104,12 @@ DROP USER user1,user3 query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} -user3 · {} -user4 · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} +user3 · {} +user4 · {} statement ok CREATE USER user1 @@ -137,10 +137,10 @@ PREPARE du AS DROP USER $1; query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} user testuser diff --git a/pkg/sql/logictest/testdata/logic_test/role b/pkg/sql/logictest/testdata/logic_test/role index b6e1ee2e2176..37e7db7b374e 100644 --- a/pkg/sql/logictest/testdata/logic_test/role +++ b/pkg/sql/logictest/testdata/logic_test/role @@ -27,11 +27,11 @@ CREATE ROLE myrole query TTT colnames SHOW ROLES ---- -username options member_of -admin CREATEROLE {} -myrole NOLOGIN {} -root CREATEROLE {admin} -testuser · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +myrole NOLOGIN {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} statement error a role/user named myrole already exists CREATE ROLE myrole @@ -57,11 +57,11 @@ DROP ROLE admin, myrole query TTT colnames SHOW ROLES ---- -username options member_of -admin CREATEROLE {} -myrole NOLOGIN {} -root CREATEROLE {admin} -testuser · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +myrole NOLOGIN {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} statement ok DROP ROLE myrole @@ -69,10 +69,10 @@ DROP ROLE myrole query TTT colnames SHOW ROLES ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} statement error pq: role/user myrole does not exist DROP ROLE myrole @@ -104,10 +104,10 @@ DROP ROLE rolea, roleb query TTT colnames SHOW ROLES ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} statement ok CREATE USER testuser2 @@ -449,13 +449,13 @@ roled testuser false query TTT SHOW ROLES ---- -admin CREATEROLE {} -roleb NOLOGIN {} -roled NOLOGIN {} -rolee NOLOGIN {} -root CREATEROLE {admin} -testuser · {roled} -testuser2 · {} +admin CREATEROLE, SETPASSWORD {} +roleb NOLOGIN {} +roled NOLOGIN {} +rolee NOLOGIN {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {roled} +testuser2 · {} statement ok DROP ROLE roleb @@ -1016,8 +1016,10 @@ CREATE ROLE rolewithlogin LOGIN query TTT SELECT * FROM system.role_options ---- -admin CREATEROLE NULL -root CREATEROLE NULL +admin CREATEROLE NULL +admin SETPASSWORD NULL +root CREATEROLE NULL +root SETPASSWORD NULL statement ok CREATE ROLE rolewithnologin NOLOGIN @@ -1025,9 +1027,11 @@ CREATE ROLE rolewithnologin NOLOGIN query TTT SELECT * FROM system.role_options ---- -admin CREATEROLE NULL -rolewithnologin NOLOGIN NULL -root CREATEROLE NULL +admin CREATEROLE NULL +admin SETPASSWORD NULL +rolewithnologin NOLOGIN NULL +root CREATEROLE NULL +root SETPASSWORD NULL statement ok ALTER ROLE rolewithlogin VALID UNTIL '2020-01-01' @@ -1036,9 +1040,11 @@ query TTT SELECT * FROM system.role_options ---- admin CREATEROLE NULL +admin SETPASSWORD NULL rolewithlogin VALID UNTIL 2020-01-01 00:00:00+00:00 rolewithnologin NOLOGIN NULL root CREATEROLE NULL +root SETPASSWORD NULL statement ok ALTER ROLE rolewithlogin VALID UNTIL NULL @@ -1047,9 +1053,11 @@ query TTT SELECT * FROM system.role_options ---- admin CREATEROLE NULL +admin SETPASSWORD NULL rolewithlogin VALID UNTIL NULL rolewithnologin NOLOGIN NULL root CREATEROLE NULL +root SETPASSWORD NULL statement ok DROP ROLE rolewithlogin @@ -1057,9 +1065,11 @@ DROP ROLE rolewithlogin query TTT SELECT * FROM system.role_options ---- -admin CREATEROLE NULL -rolewithnologin NOLOGIN NULL -root CREATEROLE NULL +admin CREATEROLE NULL +admin SETPASSWORD NULL +rolewithnologin NOLOGIN NULL +root CREATEROLE NULL +root SETPASSWORD NULL statement error pq: conflicting role options CREATE ROLE thisshouldntwork LOGIN NOLOGIN @@ -1200,3 +1210,103 @@ INSERT INTO publicdb.publictable VALUES (1) query TTT SHOW TABLES FROM publicdb ---- + +subtest setpassword_privilege + +user root + +statement ok +CREATE ROLE pseudo_admin; + ALTER ROLE pseudo_admin CREATEROLE; + DROP USER testuser2; + DROP USER testuser3; + GRANT pseudo_admin TO testuser + +user testuser + +# By default, a new role does not have privilege SETPASSWORD. + +statement ok +CREATE USER testuser2 + +statement error user testuser does not have SETPASSWORD privilege +CREATE USER testuser3 WITH PASSWORD 'abc' + +statement error user testuser does not have SETPASSWORD privilege +ALTER USER testuser2 WITH PASSWORD 'abc' + +statement error user testuser does not have SETPASSWORD privilege +ALTER USER testuser2 VALID UNTIL '2021-01-01' + +statement ok +CREATE ROLE otherrole + +statement error user testuser does not have SETPASSWORD privilege +ALTER ROLE otherrole SETPASSWORD + +statement error user testuser does not have SETPASSWORD privilege +ALTER ROLE otherrole NOSETPASSWORD + +statement error user testuser does not have SETPASSWORD privilege +CREATE ROLE otherrole2 SETPASSWORD + +statement error user testuser does not have SETPASSWORD privilege +CREATE ROLE otherrole2 NOSETPASSWORD + +user root + +statement ok +ALTER ROLE pseudo_admin SETPASSWORD + +user testuser + +statement ok +CREATE USER testuser3 WITH PASSWORD 'abc' + +statement ok +ALTER USER testuser3 WITH PASSWORD 'xyz' + +statement ok +ALTER USER testuser3 VALID UNTIL '2021-01-01' + +statement ok +ALTER ROLE otherrole SETPASSWORD + +statement ok +ALTER ROLE otherrole NOSETPASSWORD + +statement ok +CREATE ROLE otherrole2 SETPASSWORD + +statement ok +CREATE ROLE otherrole3 NOSETPASSWORD + +# If SETPASSWORD is revoked, the changes are prevented again. + +user root + +statement ok +ALTER ROLE pseudo_admin NOSETPASSWORD + +user testuser + +statement error user testuser does not have SETPASSWORD privilege +CREATE USER testuser4 WITH PASSWORD 'abc' + +statement error user testuser does not have SETPASSWORD privilege +ALTER USER testuser2 WITH PASSWORD 'abc' + +statement error user testuser does not have SETPASSWORD privilege +ALTER USER testuser2 VALID UNTIL '2021-01-01' + +statement error user testuser does not have SETPASSWORD privilege +ALTER ROLE otherrole SETPASSWORD + +statement error user testuser does not have SETPASSWORD privilege +ALTER ROLE otherrole NOSETPASSWORD + +statement error user testuser does not have SETPASSWORD privilege +CREATE ROLE otherrole4 SETPASSWORD + +statement error user testuser does not have SETPASSWORD privilege +CREATE ROLE otherrole4 NOSETPASSWORD diff --git a/pkg/sql/logictest/testdata/logic_test/show_source b/pkg/sql/logictest/testdata/logic_test/show_source index 9eeab31a58e0..bfaaa80c0cc1 100644 --- a/pkg/sql/logictest/testdata/logic_test/show_source +++ b/pkg/sql/logictest/testdata/logic_test/show_source @@ -335,10 +335,10 @@ v CREATE VIEW v (id) AS SELECT id FROM system.public.descriptor query TTT colnames SELECT * FROM [SHOW USERS] ORDER BY 1 ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} query TTTI colnames diff --git a/pkg/sql/logictest/testdata/logic_test/user b/pkg/sql/logictest/testdata/logic_test/user index 2e7333f6b289..a6ca00d5cb1c 100644 --- a/pkg/sql/logictest/testdata/logic_test/user +++ b/pkg/sql/logictest/testdata/logic_test/user @@ -3,10 +3,10 @@ query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} statement ok CREATE USER user1 @@ -14,11 +14,11 @@ CREATE USER user1 query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -root CREATEROLE {admin} -testuser · {} -user1 · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} +user1 · {} statement error pgcode 42710 a role/user named admin already exists CREATE USER admin @@ -79,16 +79,16 @@ EXECUTE chpw('blix', 'blah') query TTT colnames SHOW USERS ---- -username options member_of -admin CREATEROLE {} -foo · {} -foo-bar · {} -root CREATEROLE {admin} -testuser · {} -user1 · {} -user2 · {} -user3 · {} -ομηρος · {} +username options member_of +admin CREATEROLE, SETPASSWORD {} +foo · {} +foo-bar · {} +root CREATEROLE, SETPASSWORD {admin} +testuser · {} +user1 · {} +user2 · {} +user3 · {} +ομηρος · {} statement error no username specified CREATE USER "" @@ -151,11 +151,13 @@ ALTER USER user4 NOLOGIN query TTT SELECT * FROM system.role_options ---- -admin CREATEROLE NULL -root CREATEROLE NULL -testuser CREATEROLE NULL -user4 CREATEROLE NULL -user4 NOLOGIN NULL +admin CREATEROLE NULL +admin SETPASSWORD NULL +root CREATEROLE NULL +root SETPASSWORD NULL +testuser CREATEROLE NULL +user4 CREATEROLE NULL +user4 NOLOGIN NULL statement ok DROP USER user4 diff --git a/pkg/sql/parser/parse_test.go b/pkg/sql/parser/parse_test.go index 6c895742af8e..e7cd42b7c2c0 100644 --- a/pkg/sql/parser/parse_test.go +++ b/pkg/sql/parser/parse_test.go @@ -2174,6 +2174,10 @@ $function$`, `ALTER ROLE 'foo' WITH CREATEROLE`}, {`ALTER ROLE foo CREATEROLE`, `ALTER ROLE 'foo' WITH CREATEROLE`}, + {`ALTER ROLE foo SETPASSWORD`, + `ALTER ROLE 'foo' WITH SETPASSWORD`}, + {`ALTER ROLE foo NOSETPASSWORD`, + `ALTER ROLE 'foo' WITH NOSETPASSWORD`}, {`DROP ROLE foo, bar`, `DROP ROLE 'foo', 'bar'`}, {`DROP ROLE IF EXISTS foo, bar`, diff --git a/pkg/sql/parser/sql.y b/pkg/sql/parser/sql.y index bf36fd386003..e30cfd1b6511 100644 --- a/pkg/sql/parser/sql.y +++ b/pkg/sql/parser/sql.y @@ -600,7 +600,7 @@ func (u *sqlSymUnion) alterTypeAddValuePlacement() *tree.AlterTypeAddValuePlacem %token MULTILINESTRING MULTIPOINT MULTIPOLYGON %token NAN NAME NAMES NATURAL NEXT NO NOCREATEROLE NOLOGIN NO_INDEX_JOIN -%token NONE NORMAL NOT NOTHING NOTNULL NOWAIT NULL NULLIF NULLS NUMERIC +%token NONE NORMAL NOSETPASSWORD NOT NOTHING NOTNULL NOWAIT NULL NULLIF NULLS NUMERIC %token OF OFF OFFSET OID OIDS OIDVECTOR ON ONLY OPT OPTION OPTIONS OR %token ORDER ORDINALITY OTHERS OUT OUTER OVER OVERLAPS OVERLAY OWNED OWNER OPERATOR @@ -618,7 +618,7 @@ func (u *sqlSymUnion) alterTypeAddValuePlacement() *tree.AlterTypeAddValuePlacem %token ROLE ROLES ROLLBACK ROLLUP ROW ROWS RSHIFT RULE %token SAVEPOINT SCATTER SCHEMA SCHEMAS SCRUB SEARCH SECOND SELECT SEQUENCE SEQUENCES -%token SERIALIZABLE SERVER SESSION SESSIONS SESSION_USER SET SETTING SETTINGS +%token SERIALIZABLE SERVER SESSION SESSIONS SESSION_USER SET SETPASSWORD SETTING SETTINGS %token SHARE SHOW SIMILAR SIMPLE SKIP SMALLINT SMALLSERIAL SNAPSHOT SOME SPLIT SQL %token START STATISTICS STATUS STDIN STRICT STRING STORAGE STORE STORED STORING SUBSTRING @@ -5573,17 +5573,25 @@ role_option: $$.val = tree.KVOption{Key: tree.Name($1), Value: nil} } | NOCREATEROLE - { - $$.val = tree.KVOption{Key: tree.Name($1), Value: nil} - } + { + $$.val = tree.KVOption{Key: tree.Name($1), Value: nil} + } | LOGIN - { - $$.val = tree.KVOption{Key: tree.Name($1), Value: nil} - } + { + $$.val = tree.KVOption{Key: tree.Name($1), Value: nil} + } | NOLOGIN - { - $$.val = tree.KVOption{Key: tree.Name($1), Value: nil} - } + { + $$.val = tree.KVOption{Key: tree.Name($1), Value: nil} + } +| SETPASSWORD + { + $$.val = tree.KVOption{Key: tree.Name($1), Value: nil} + } +| NOSETPASSWORD + { + $$.val = tree.KVOption{Key: tree.Name($1), Value: nil} + } | password_clause | valid_until_clause @@ -10368,6 +10376,7 @@ unreserved_keyword: | NO_INDEX_JOIN | NOCREATEROLE | NOLOGIN +| NOSETPASSWORD | NOWAIT | NULLS | IGNORE_FOREIGN_KEYS @@ -10421,6 +10430,7 @@ unreserved_keyword: | ROLLUP | ROWS | RULE +| SETPASSWORD | SETTING | SETTINGS | STATUS diff --git a/pkg/sql/pgwire/pgwire_test.go b/pkg/sql/pgwire/pgwire_test.go index ebe7ee22d6d2..cc256b76ddb6 100644 --- a/pkg/sql/pgwire/pgwire_test.go +++ b/pkg/sql/pgwire/pgwire_test.go @@ -570,8 +570,10 @@ func TestPGPreparedQuery(t *testing.T) { baseTest.SetArgs("woo", "waa"), }}, {"SHOW USERS", []preparedQueryTest{ - baseTest.Results("abc", "", "{}").Results("admin", "CREATEROLE", "{}"). - Results("root", "CREATEROLE", "{admin}").Results("woo", "", "{}"), + baseTest.Results("abc", "", "{}"). + Results("admin", "CREATEROLE, SETPASSWORD", "{}"). + Results("root", "CREATEROLE, SETPASSWORD", "{admin}"). + Results("woo", "", "{}"), }}, {"DROP USER $1", []preparedQueryTest{ baseTest.SetArgs("abc"), diff --git a/pkg/sql/roleoption/option_string.go b/pkg/sql/roleoption/option_string.go index efc2f84f8b3c..11e3e9863459 100644 --- a/pkg/sql/roleoption/option_string.go +++ b/pkg/sql/roleoption/option_string.go @@ -14,11 +14,13 @@ func _() { _ = x[LOGIN-4] _ = x[NOLOGIN-5] _ = x[VALIDUNTIL-6] + _ = x[SETPASSWORD-7] + _ = x[NOSETPASSWORD-8] } -const _Option_name = "CREATEROLENOCREATEROLEPASSWORDLOGINNOLOGINVALIDUNTIL" +const _Option_name = "CREATEROLENOCREATEROLEPASSWORDLOGINNOLOGINVALIDUNTILSETPASSWORDNOSETPASSWORD" -var _Option_index = [...]uint8{0, 10, 22, 30, 35, 42, 52} +var _Option_index = [...]uint8{0, 10, 22, 30, 35, 42, 52, 63, 76} func (i Option) String() string { i -= 1 diff --git a/pkg/sql/roleoption/role_option.go b/pkg/sql/roleoption/role_option.go index 82402d8a6082..689f166b8c28 100644 --- a/pkg/sql/roleoption/role_option.go +++ b/pkg/sql/roleoption/role_option.go @@ -42,16 +42,20 @@ const ( LOGIN NOLOGIN VALIDUNTIL + SETPASSWORD + NOSETPASSWORD ) // toSQLStmts is a map of Kind -> SQL statement string for applying the // option to the role. var toSQLStmts = map[Option]string{ - CREATEROLE: `UPSERT INTO system.role_options (username, option) VALUES ($1, 'CREATEROLE')`, - NOCREATEROLE: `DELETE FROM system.role_options WHERE username = $1 AND option = 'CREATEROLE'`, - LOGIN: `DELETE FROM system.role_options WHERE username = $1 AND option = 'NOLOGIN'`, - NOLOGIN: `UPSERT INTO system.role_options (username, option) VALUES ($1, 'NOLOGIN')`, - VALIDUNTIL: `UPSERT INTO system.role_options (username, option, value) VALUES ($1, 'VALID UNTIL', $2::timestamptz::string)`, + CREATEROLE: `UPSERT INTO system.role_options (username, option) VALUES ($1, 'CREATEROLE')`, + NOCREATEROLE: `DELETE FROM system.role_options WHERE username = $1 AND option = 'CREATEROLE'`, + LOGIN: `DELETE FROM system.role_options WHERE username = $1 AND option = 'NOLOGIN'`, + NOLOGIN: `UPSERT INTO system.role_options (username, option) VALUES ($1, 'NOLOGIN')`, + SETPASSWORD: `UPSERT INTO system.role_options (username, option) VALUES ($1, 'SETPASSWORD')`, + NOSETPASSWORD: `DELETE FROM system.role_options WHERE username = $1 AND option = 'SETPASSWORD'`, + VALIDUNTIL: `UPSERT INTO system.role_options (username, option, value) VALUES ($1, 'VALID UNTIL', $2::timestamptz::string)`, } // Mask returns the bitmask for a given role option. @@ -61,12 +65,14 @@ func (o Option) Mask() uint32 { // ByName is a map of string -> kind value. var ByName = map[string]Option{ - "CREATEROLE": CREATEROLE, - "NOCREATEROLE": NOCREATEROLE, - "PASSWORD": PASSWORD, - "LOGIN": LOGIN, - "NOLOGIN": NOLOGIN, - "VALID_UNTIL": VALIDUNTIL, + "CREATEROLE": CREATEROLE, + "NOCREATEROLE": NOCREATEROLE, + "PASSWORD": PASSWORD, + "LOGIN": LOGIN, + "NOLOGIN": NOLOGIN, + "SETPASSWORD": SETPASSWORD, + "NOSETPASSWORD": NOSETPASSWORD, + "VALID_UNTIL": VALIDUNTIL, } // ToOption takes a string and returns the corresponding Option. @@ -152,10 +158,9 @@ func (rol List) CheckRoleOptionConflicts() error { return err } - if (roleOptionBits&CREATEROLE.Mask() != 0 && - roleOptionBits&NOCREATEROLE.Mask() != 0) || - (roleOptionBits&LOGIN.Mask() != 0 && - roleOptionBits&NOLOGIN.Mask() != 0) { + if (roleOptionBits&CREATEROLE.Mask() != 0 && roleOptionBits&NOCREATEROLE.Mask() != 0) || + (roleOptionBits&SETPASSWORD.Mask() != 0 && roleOptionBits&NOSETPASSWORD.Mask() != 0) || + (roleOptionBits&LOGIN.Mask() != 0 && roleOptionBits&NOLOGIN.Mask() != 0) { return pgerror.Newf(pgcode.Syntax, "conflicting role options") } return nil diff --git a/pkg/sqlmigrations/migrations.go b/pkg/sqlmigrations/migrations.go index 59ebfe1e6c6a..449687415f2f 100644 --- a/pkg/sqlmigrations/migrations.go +++ b/pkg/sqlmigrations/migrations.go @@ -317,7 +317,7 @@ var backwardCompatibleMigrations = []migrationDescriptor{ { // Introduced in v20.1. name: "add CREATEROLE privilege to admin/root", - workFn: addCreateRoleToAdminAndRoot, + workFn: func(ctx context.Context, r runner) error { return addOptionToAdminAndRoot(ctx, r, "CREATEROLE") }, }, { // Introduced in v20.2. @@ -333,6 +333,11 @@ var backwardCompatibleMigrations = []migrationDescriptor{ includedInBootstrap: clusterversion.VersionByKey(clusterversion.VersionAddScheduledJobsTable), newDescriptorIDs: staticIDs(keys.ScheduledJobsTableID), }, + { + // Introduced in v20.2. + name: "add SETPASSWORD privilege to admin/root", + workFn: func(ctx context.Context, r runner) error { return addOptionToAdminAndRoot(ctx, r, "SETPASSWORD") }, + }, } func staticIDs( @@ -1587,25 +1592,29 @@ func createRoleOptionsTable(ctx context.Context, r runner) error { return nil } -func addCreateRoleToAdminAndRoot(ctx context.Context, r runner) error { +func addOptionToAdminAndRoot(ctx context.Context, r runner, option string) error { // Upsert the admin/root roles with CreateRole privilege into the table. // We intentionally override any existing entry. const upsertCreateRoleStmt = ` - UPSERT INTO system.role_options (username, option, value) VALUES ($1, 'CREATEROLE', NULL) + UPSERT INTO system.role_options (username, option, value) VALUES ($1, $2, NULL) ` err := r.execAsRootWithRetry(ctx, - "add role options table and upsert admin with CREATEROLE", + "add role options table and upsert admin with "+option, upsertCreateRoleStmt, - sqlbase.AdminRole) + sqlbase.AdminRole, + option, + ) if err != nil { return err } return r.execAsRootWithRetry(ctx, - "add role options table and upsert admin with CREATEROLE", + "add role options table and upsert admin with "+option, upsertCreateRoleStmt, - security.RootUser) + security.RootUser, + option, + ) } func createReportsMetaTable(ctx context.Context, r runner) error {