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

sql: OWNED BY SESSION_USER/CURRENT_USER does not work #54696

Closed
arulajmani opened this issue Sep 23, 2020 · 7 comments · Fixed by #70439
Closed

sql: OWNED BY SESSION_USER/CURRENT_USER does not work #54696

arulajmani opened this issue Sep 23, 2020 · 7 comments · Fixed by #70439
Assignees
Labels
A-sql-privileges SQL privilege handling and permission checks. C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. E-starter Might be suitable for a starter project for new employees or team members. T-sql-foundations SQL Foundations Team (formerly SQL Schema + SQL Sessions)

Comments

@arulajmani
Copy link
Collaborator

arulajmani commented Sep 23, 2020

CURRENT_USER/SESSION_USER should be special keywords, but our current syntax treats them as normal strings. Consider the following example using the ALTER SCHEMA command:

root@127.0.0.1:61524/movr> alter schema sc OWNER TO CURRENT_USER;
ERROR: role/user "current_user" does not exist
SQLSTATE: 42704
root@127.0.0.1:61524/movr> alter schema sc OWNER TO SESSION_USER;
ERROR: role/user "session_user" does not exist
SQLSTATE: 42704
root@127.0.0.1:61524/movr> alter schema sc OWNER TO admin;
ALTER SCHEMA

Time: 0.029s total (exec 10.2% / net 88.2% / other 1.6%)

Epic CRDB-8948

@arulajmani arulajmani added A-sql-privileges SQL privilege handling and permission checks. C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. labels Sep 23, 2020
@cockroachdb cockroachdb deleted a comment from blathers-crl bot Sep 23, 2020
@arulajmani arulajmani changed the title sql: ALTER SCHEMA OWNED BY SESSION_USER/CURRENT_USER does not work sql: OWNED BY SESSION_USER/CURRENT_USER does not work Sep 23, 2020
@angelapwen
Copy link

Curious about this one — the syntax does parse it with role_spec which takes in any non-reserved string, CURRENT_USER, or SESSION_USER (and is also used in many other situations where this is the case).

@angelapwen
Copy link

@knz you mentioned in #55382 that DROP ROLE handles this case appropriately. This is my output when I tested:

demo@127.0.0.1:52133/movr> DROP ROLE SESSION_USER;
invalid syntax: statement ignored: at or near "session_user": syntax error
SQLSTATE: 42601
DETAIL: source SQL:
DROP ROLE SESSION_USER
                      ^
HINT: try \h DROP ROLE

I don't know that this is the right error either — did you get this output?

@knz
Copy link
Contributor

knz commented Oct 15, 2020

I meant that DROP ROLE doesn't use simple strings for its usernames. Instead it allows names to be expressions, in particular so that the SQL client can use placeholders.

The key piece of parser insight is this:

%type <tree.Expr> string_or_placeholder_list
...
string_or_placeholder:
  non_reserved_word_or_sconst
  {
    $$.val = tree.NewStrVal($1)
  }
| PLACEHOLDER
  {
    p := $1.placeholder()
    sqllex.(*lexer).UpdateNumPlaceholders(p)
    $$.val = p
  }

string_or_placeholder_list:
  string_or_placeholder
  {
    $$.val = tree.Exprs{$1.expr()}
  }
| string_or_placeholder_list ',' string_or_placeholder
  {
    $$.val = append($1.exprs(), $3.expr())
  }
drop_role_stmt:
  DROP ROLE string_or_placeholder_list
  {
    $$.val = &tree.DropRole{Names: $3.exprs(), IfExists: false, IsRole: $2.bool()}
  }

And the key piece of useful Go code is this:

// DropRoleNode creates a "drop user" plan node. This can be called from DROP USER or DROP ROLE.
func (p *planner) DropRoleNode(...) {
       ...
       names, err := p.TypeAsStringArray(ctx, namesE, opName)
       ...
}

func (n *DropRoleNode) startExec(params runParams) error {
       ...
        names, err := n.names()
}

I think all our role handling throughout SQL should ensure that names are handled this way.

@knz
Copy link
Contributor

knz commented Oct 15, 2020

This code does not Currently handle session_user / current_user but the way to handle this is to make the string_or_placeholder rule instantiate a FuncExpr for it.

craig bot pushed a commit that referenced this issue Oct 21, 2020
55816: sql: allow parsing of DROP OWNED BY r=otan a=angelapwen

Part 1 of addressing #55381.

Add parsing and telemetry support for DROP OWNED BY command.
Includes small refactors of the related REASSIGN OWNED BY
command such as appending "_by" to some instances of
"reassign_owned" for readability.

Plan to follow up with implementation of DROP OWNED BY for
feature parity with PostgresQL, which will resolve the issue
linked.

Note that this command is also making use of `role_spec_list` 
which as noted in #54696 does not properly parse 
`CURRENT_USER` or `SESSION_USER` and uses strings for
roles/usernames rather than expressions. This issue is out of 
scope for this PR and will be resolved separately.

Release note: None

Co-authored-by: angelapwen <angelaw@cockroachlabs.com>
craig bot pushed a commit that referenced this issue Oct 27, 2020
55398:  *: make conversions between `string` and usernames more explicit  r=aaron-crl,arulajmani a=knz

Informs (but does not fix) #55396

Informs #54696
Informs  #55389

tldr: the conversions between "external" strings and internal
usernames was unprincipled, and it turns out, incorrect in some
cases. This patch cleans this up by introducing a strict conversion
API.

**Background**

CockroachDB currently performs case folding and unicode NFC
normalization upon receiving usernames, specifically usernames received
from as SQL login principals.

Internally, usernames are often—but not always—considered
pre-normalized for the purpose of comparisons, privilege checks, role
membership checks and the like.

Finally, sometimes usernames are reported "back to outside".
In error messages, log files etc, but also:

- in the SQL syntax produced by SHOW CREATE, SHOW SYNTAX etc.
- to generate OS-level file names for exported files.

**New API**

This patch introduces a new data type `security.SQLUsername`.
It is incomparable and non-convertible with the Go `string`.

Engineers must now declare intent about how to do the conversion:

- `security.MakeSQLUsernameFromUserInput` converts an "outside" string
  to a username.
- `security.MakeSQLUsernameFromPreNormalizedString` promises that
  its argument has already been previously normalized.
- `security.MakeSQLUsernameFromPreNormalizedStringChecked` also checks
  that its argument has already been previously normalized.

To output usernames, the following APIs are also available.

- `username.Normalized()` produces the username itself, without
  decorations. These corresponds to the raw string (after
  normalization).

- `username.SQLIdentifier()` produces the username in valid
  SQL syntax, so that it can be injected safely in a SQL
  statement.

- `(*tree.FmtCtx).FormatUsername()` takes a username and properly
  handles quoting and anonymization, like `FormatName()` does for
  `tree.Name` already.

Likewise, conversion from/to protobuf is now regulated, via the new
APIs `username.EncodeProto()` and `usernameproto.Decode()`.

**Problems being solved**

- the usernames "from outside" were normalized sometimes, *but not
  consistently*:

  1. they were in the arguments of CREATE/DROP/ALTER ROLE. This was
     not changed.
  2. they were not consistently converted in `cockroach cert`. This was
     corrected.
  3. they were not in the `cockroach userfile` commands. This
     has been adjusted with a reference to issue #55389.
  4. they are *not* in GRANT/REVOKE. This patch does not change
     this behavior, but highlights it by spelling out
     `MakeSQLUsernameFromPreNormalizedString()` in the implementation.
  5. ditto for CREATE SCHEMA ... AUTHORIZATION and ALTER ... OWNER TO
  6. they were in the argument to `LoginRequest`. This was not
     changed.
  7. they were not in the argument of the other API requests that
     allow usernames, for example `ListSessions` or `CancelQuery`.
     This was not changed, but is now documented in the API.

- the usernames "inside" were incorrectly directly injected
  in SQL statements, even though they may contain special
  characters that create SQL syntax errors.

  This has been corrected by suitable uses of the new
  `SQLIdentifier()` method.

- There was an outright bug in a call to `CreateGCJobRec` (something
  about GCing jobs), where a `Description` field was passed in lieu
  of a username for a `User` field. The implications of this are unclear.

**Status after this change**

The new API makes it possible to audit exactly where "sensitive"
username/string conversion occurs. After this patch, we find the
following uses:

- `MakeSQLUsernameFromUserInput`:

  - pgwire user auth
  - CLI URL parsing
  - `cockroach userfile`
  - `cockroach cert`
  - `(*rpc.SecurityContext).PGURL()` (unsure whether that's a good thing)
  - CREATE/DROP/ALTER ROLE
  - when using literal strings as `role_spec` in the SQL grammar

- `MakeSQLUsernameFromPreNormalizedString`:

  - role membership checks inside SQL based on data read
    from `system` tables.
  - in GRANT/REVOKE (this is surprising, see above)

- `MakeSQLUsernameFromPreNormalizedStringChecked`:

  - when intepreting the username in API query parameters, for
    those API documented as using pre-normalized usernames.

Release note: None

55986: opt: use paired joins for left semi inverted joins r=sumeerbhola a=sumeerbhola

Semi joins using the inverted index used to be converted
to an inner inverted join followed by an inner lookup join
and a distinct for de-duplication. They are now converted
to paired-joins consisting of an inner inverted join
followed by a left semi lookup join, which is more
efficient.

Release note: None

55997: sql: fix object lookup for ALTER TABLE ... RENAME TO ... r=ajwerner a=jayshrivastava

Previously, ALTER TABLE schema.table RENAME TO schema.existing_table
would fail because the statement would look up the destination table in
the public schema only. This commit fixes this behavior by using
the correct destination schema.

Fixes #55985

Release note: None


Co-authored-by: Raphael 'kena' Poss <knz@thaumogen.net>
Co-authored-by: sumeerbhola <sumeer@cockroachlabs.com>
Co-authored-by: Jayant Shrivastava <jayants@cockroachlabs.com>
@angelapwen angelapwen removed their assignment Apr 21, 2021
@angelapwen
Copy link

I'm tying up loose ends as I offboard this week and want to make sure that this issue is being tracked in the new SQL Experience board. I've removed myself from assigned so others will feel free to take it on. Thank you ✨

@rafiss rafiss added the T-sql-foundations SQL Foundations Team (formerly SQL Schema + SQL Sessions) label May 12, 2021
@rafiss rafiss added the E-starter Might be suitable for a starter project for new employees or team members. label May 20, 2021
@rafiss
Copy link
Collaborator

rafiss commented Jul 23, 2021

I found that Postgres does not use a function for SESSION_USER or CURRENT_USER. The parser instead looks for these tokens and makes a "special" RoleSpec: https://github.com/postgres/postgres/blob/6beb38cfc9ddd4cd3d2eb5402981ebdd69a618b4/src/backend/parser/gram.y#L15315-L15326

When the RoleSpec is accessed, these types are special-cased: https://github.com/postgres/postgres/blob/6beb38cfc9ddd4cd3d2eb5402981ebdd69a618b4/src/backend/utils/adt/acl.c#L5128-L5146

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-sql-privileges SQL privilege handling and permission checks. C-bug Code not up to spec/doc, specs & docs deemed correct. Solution expected to change code/behavior. E-starter Might be suitable for a starter project for new employees or team members. T-sql-foundations SQL Foundations Team (formerly SQL Schema + SQL Sessions)
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants