Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release-20.2: sql: Fix privilege checks to consider direct/indirect roles #58512

Merged
merged 1 commit into from
Jan 7, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions pkg/sql/exec_util.go
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,14 @@ func golangFillQueryArguments(args ...interface{}) (tree.Datums, error) {
switch {
case val.IsNil():
d = tree.DNull
case val.Type().Elem().Kind() == reflect.String:
a := tree.NewDArray(types.String)
for v := 0; v < val.Len(); v++ {
if err := a.Append(tree.NewDString(val.Index(v).String())); err != nil {
return nil, err
}
}
d = a
case val.Type().Elem().Kind() == reflect.Uint8:
d = tree.NewDBytes(tree.DBytes(val.Bytes()))
}
Expand Down
7 changes: 7 additions & 0 deletions pkg/sql/faketreeeval/evalctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,13 @@ func (ep *DummyEvalPlanner) UnsafeDeleteNamespaceEntry(
return errors.WithStack(errEvalPlanner)
}

// MemberOfWithAdminOption is part of the EvalPlanner interface.
func (ep *DummyEvalPlanner) MemberOfWithAdminOption(
ctx context.Context, member string,
) (map[string]bool, error) {
return nil, errors.WithStack(errEvalPlanner)
}

var _ tree.EvalPlanner = &DummyEvalPlanner{}

var errEvalPlanner = pgerror.New(pgcode.ScalarOperationCannotRunWithoutFullSessionContext,
Expand Down
74 changes: 74 additions & 0 deletions pkg/sql/logictest/testdata/logic_test/privilege_builtins
Original file line number Diff line number Diff line change
Expand Up @@ -1054,3 +1054,77 @@ query B
SELECT has_column_privilege('hcp_test'::REGCLASS, 4, 'SELECT')
----
true

# Regression test for #58254. Tests privilege inheritance for roles (direct and indirect).

user root

statement ok
DROP DATABASE IF EXISTS my_db;
CREATE DATABASE my_db;

statement ok
CREATE ROLE my_role;

statement ok
GRANT CREATE ON DATABASE my_db TO my_role;
GRANT my_role TO testuser;

user testuser

statement ok
use my_db

statement ok
CREATE SCHEMA s;
CREATE TABLE s.t()

# Privilege check on direct member of role with CREATE privilege granted.
query B
SELECT has_schema_privilege('testuser', 's', 'create')
----
true

# Privilege check on direct member of role without USAGE privilege granted.
query B
SELECT has_schema_privilege('testuser', 's', 'usage')
----
false

# Confirm privilege checks on all objects.
query BBB
SELECT has_database_privilege('testuser', 'my_db', 'create'),
has_schema_privilege('testuser', 'public', 'usage'),
has_table_privilege('testuser', 's.t', 'select')
----
true false false

user root

statement ok
use my_db;
CREATE USER testuser2;
GRANT testuser TO testuser2;
GRANT ALL ON SCHEMA s TO testuser2
Copy link

@angelapwen angelapwen Jan 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems to be failing here

testdata/logic_test/privilege_builtins:1104:
expected success, but found
(3F000) unknown schema "s"
errors.go:60: in NewUndefinedSchemaError()
logic.go:2693:
testdata/logic_test/privilege_builtins:1104: error while processing
logic.go:2693: pq: unknown schema "s"

Does it need to be my_db.s instead of just s? I'm not sure why it fails in 20.2 but not in the original PR.

Copy link
Collaborator Author

@rafiss rafiss Jan 7, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah i see... i had to remove that because this isn't (and won't be) backported to 20.2: #55647

hmm but i would have expected it to work since the current db is my_db... will investigate

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ah i see, probably because the user was switched to root.


user testuser2

statement ok
use my_db

# Make sure testuser only has CREATE privilege on schema s but testuser2 has both CREATE and USAGE.
query BBBB
SELECT has_schema_privilege('testuser', 's', 'create'),
has_schema_privilege('testuser', 's', 'usage'),
has_schema_privilege('testuser2', 's', 'create'),
has_schema_privilege('testuser2', 's', 'usage')
----
true false true true

# Confirm privilege checks on all objects with testuser2, should match testuser.
query BBB
SELECT has_database_privilege('testuser2', 'my_db', 'create'),
has_schema_privilege('testuser2', 'public', 'usage'),
has_table_privilege('testuser2', 's.t', 'select')
----
true false false
16 changes: 14 additions & 2 deletions pkg/sql/sem/builtins/pg_builtins.go
Original file line number Diff line number Diff line change
Expand Up @@ -521,13 +521,25 @@ func evalPrivilegeCheck(
if withGrantOpt {
privChecks = append(privChecks, privilege.GRANT)
}

allRoleMemberships, err := ctx.Planner.MemberOfWithAdminOption(ctx.Context, user)
if err != nil {
return nil, err
}

// Slice containing all roles user is a direct and indirect member of.
allRoles := []string{security.PublicRole, user}
for role := range allRoleMemberships {
allRoles = append(allRoles, role)
}

for _, p := range privChecks {
query := fmt.Sprintf(`
SELECT bool_or(privilege_type IN ('%s', '%s')) IS TRUE
FROM information_schema.%s WHERE grantee IN ($1, $2) AND %s`,
FROM information_schema.%s WHERE grantee = ANY ($1) AND %s`,
privilege.ALL, p, infoTable, pred)
r, err := ctx.InternalExecutor.QueryRow(
ctx.Ctx(), "eval-privilege-check", ctx.Txn, query, security.PublicRole, user,
ctx.Ctx(), "eval-privilege-check", ctx.Txn, query, allRoles,
)
if err != nil {
return nil, err
Expand Down
8 changes: 8 additions & 0 deletions pkg/sql/sem/tree/eval.go
Original file line number Diff line number Diff line change
Expand Up @@ -3017,6 +3017,14 @@ type EvalPlanner interface {
descID int64,
force bool,
) error

// MemberOfWithAdminOption is used to collect a list of roles (direct and
// indirect) that the member is part of. See the comment on the planner
// implementation in authorization.go
MemberOfWithAdminOption(
ctx context.Context,
member string,
) (map[string]bool, error)
}

// EvalSessionAccessor is a limited interface to access session variables.
Expand Down