Skip to content

Add invoker security mode for views#12082

Closed
electrum wants to merge 1 commit intoprestodb:masterfrom
electrum:view-security
Closed

Add invoker security mode for views#12082
electrum wants to merge 1 commit intoprestodb:masterfrom
electrum:view-security

Conversation

@electrum
Copy link
Contributor

No description provided.

@electrum
Copy link
Contributor Author

cc @martint

@sopel39
Copy link
Contributor

sopel39 commented Dec 17, 2018

Is this semantics defined by SQL standard or this is Presto extension?

@JsonProperty("columns") List<ViewColumn> columns,
@JsonProperty("owner") Optional<String> owner)
@JsonProperty("owner") Optional<String> owner,
@JsonProperty("runAsInvoker") boolean runAsInvoker)
Copy link
Contributor

Choose a reason for hiding this comment

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

Do i understand correctly that existing view definitions will be parsed correctly because Jackson defaults missing boolean properties to false?

Copy link
Contributor

Choose a reason for hiding this comment

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

Am i right that ViewDefinition is what gets persisted in a Metastore?
if so, we should have tests like:

json literal → deserialization → check parsed correctly

to ensure backwards compat with view definitions stored by older Presto vesions

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, existing views will parse correctly. Good point -- I will add a test for existing views.

this.columns = ImmutableList.copyOf(requireNonNull(columns, "columns is null"));
this.owner = requireNonNull(owner, "owner is null");
this.runAsInvoker = runAsInvoker;
checkArgument(!runAsInvoker || !owner.isPresent(), "owner cannot be present with runAsInvoker");
Copy link
Contributor

Choose a reason for hiding this comment

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

Why owner was optional until now?
other than that, i would expect checkArgument(runAsInvoker == !owner.isPresent() here

Copy link
Contributor Author

Choose a reason for hiding this comment

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

It was optional because legacy views from before owner was added will not have them. I will also add a test for that.

Copy link
Contributor

Choose a reason for hiding this comment

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

That's what i suspected. Having a test would have a bonus advantage that it would self-document

{
Optional<CreateView.Security> security = Optional.empty();
if (context.DEFINER() != null) {
security = Optional.of(CreateView.Security.DEFINER);
Copy link
Contributor

Choose a reason for hiding this comment

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

In case of an { INNER? | OUTER } case, AstBuilder applies "defaulting", i.e. determines that the Join is INNER when not explicitly specified.
Please explain why you don't follow the same path for CREATE VIEW. This would simplify the code, with the obvious drawback that SqlFormatter wouldn't be able to reconstruct the original text 1-1 (not sure if this is important though). Any other reasons?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@martint has the opinion that in general, the AST should be a faithful representation of the SQL syntax and not have knowledge of semantics. Where to draw the line is a bit arbitrary. For example, we agree that "noise words" like the optional AS clause for table aliases can be omitted. I'll let him comment further.

Copy link
Contributor

@rschlussel rschlussel left a comment

Choose a reason for hiding this comment

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

Can you add more description to the commit message (like what's in the docs).

Also, is the syntax defined by the SQL standard? I think it's a bit clunky because it doesn't make sense when you read it aloud as a sentence. So if it's not required to be exactly this way, I'd prefer something more similar what we have for table properties WITH SECURITY =<security_type>.

if (view != null) {
ViewDefinition definition = deserializeView(view.getViewData());
if (view.getOwner().isPresent()) {
if (view.getOwner().isPresent() && !definition.isRunAsInvoker()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

how could a view have an owner if runAsInvoker is also set?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This is ConnectorViewDefinition which comes from the connector, not the serialized view. Connectors will set this unconditionally (they have no knowledge of the view security model). For example, in Hive, this is always set to the table owner returned by the metastore.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah, I think I understand now. Let me now if this is correct.
Viewdata doesn't contain information about the owner (for legacy views only?), so it's passed only with the connector view information. Therefore we need some other field to say whether we want to get the view owner from the connector or not, and that's why we you added the "runAsInvoker" boolean.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, exactly

@JsonProperty("columns") List<ViewColumn> columns,
@JsonProperty("owner") Optional<String> owner)
@JsonProperty("owner") Optional<String> owner,
@JsonProperty("runAsInvoker") boolean runAsInvoker)
Copy link
Contributor

Choose a reason for hiding this comment

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

What's the difference between when owner is empty and runAsInvoker is false and when owner is empty and runAsInvoker is true?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Legacy views from before owner was added will have neither of these fields. I had originally wanted to simply use "owner not present" to indicate invoker security, but that doesn't work due to legacy views.

Copy link
Contributor

Choose a reason for hiding this comment

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

But what's the difference in behavior? Won't both of them use regular access control?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I believe you answered your own question above.

@electrum
Copy link
Contributor Author

This is a Presto extension to the SQL standard. The grammar is based on the standard for SQL routines which support both modes.

@electrum
Copy link
Contributor Author

Here's an example of the standard syntax for CREATE PROCEDURE that this syntax is based on:

CREATE PROCEDURE account_count()
SQL SECURITY INVOKER
BEGIN
  SELECT count(*) FROM mysql.user;
END;

Note that the word SQL is used to differentiate from EXTERNAL, which are two different security models. I don't think we need for views, since they always use the SQL security model.

@findepi
Copy link
Contributor

findepi commented Dec 18, 2018

FWIW, Oracle has equivalent functionality using BEQUEATH keyword (https://docs.oracle.com/database/121/DBSEG/dr_ir.htm#DBSEG668):

CREATE VIEW view_name [ BEQUEATH { DEFINER | CURRENT_USER } ] AS ...

Teradata, SQL Server and PostgreSQL seem not to have the functionality.

@electrum
Copy link
Contributor Author

@findepi Nice find, I missed that. Should we use that syntax instead, both for familiarity/compatibility, and given that it is now the likely choice if the behavior is ever standardized?

@findepi
Copy link
Contributor

findepi commented Dec 19, 2018

@electrum i don't know. I am OK whatever you and @martint agree on.

BTW my second thought is with "invoker" term. It's suitable for procedures. Should we say you invoke a view?

@martint
Copy link
Contributor

martint commented Dec 19, 2018

I was having similar thoughts as @findepi. Views are not “invoked” or “called”, so the term “invoker” doesn’t seem appropriate.

There’s also the more neutral “authid” syntax in some databases (I don’tremember if it was oracle, db2 or sql server that had it)

@electrum
Copy link
Contributor Author

Oracle uses AUTHID DEFINER or AUTHID CURRENT_USER (mentioned on the page linked above) when creating procedures. It is interesting that they chose a different syntax for this feature added recently in Oracle 12c (2013) when the other syntax has been around since Oracle 8 (1997).

@electrum
Copy link
Contributor Author

SQL Server has a WITH options for procedures: https://docs.microsoft.com/en-us/sql/t-sql/statements/create-procedure-transact-sql?view=sql-server-2017

It looks like the syntax would be one of

  • CREATE PROCEDURE abc WITH EXECUTE AS OWNER ...
  • CREATE PROCEDURE abc WITH EXECUTE AS USER ...

There is appeal to having a WITH clause (as @rschlussel mentioned) and I also prefer OWNER to DEFINER since that directly matches the owner concept in SQL. However, in practice, this syntax seems rather verbose (even without the WITH), and the repeated AS clause isn't great:

  • CREATE VIEW abc WITH EXECUTE AS OWNER AS ...
  • CREATE VIEW abc WITH EXECUTE AS CURRENT_USER AS ...

@electrum
Copy link
Contributor Author

This is how the AUTHID would look using WITH:

  • CREATE VIEW abc WITH AUTHID OWNER AS ...
  • CREATE VIEW abc WITH AUTHID CURRENT_USER AS ...

But when thinking about AUTHID as a word, I actually don't know what it's supposed to mean.

@kokosing
Copy link
Contributor

What about:

CREATE VIEW abc WITH EXECUTE BY OWNER AS ...
CREATE VIEW abc WITH EXECUTE BY CURRENT_USER AS ...

?

That way AS is not repeated.

From the other hand, maybe repeating AS is a better idea, because user does not need to think which preposition they have to use.

@electrum
Copy link
Contributor Author

electrum commented Jan 3, 2019

Coming back to this with fresh eyes, I think we should copy the Oracle syntax for creating views, both for user familiarity and because it is the likely choice to be standardized (if this feature ever is).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants