[
https://issues.apache.org/jira/browse/CASSANDRA-7557?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=14393190#comment-14393190
]
Sam Tunnicliffe commented on CASSANDRA-7557:
--------------------------------------------
I've pushed a preliminary version
[here|https://github.com/beobal/cassandra/tree/7557] which depends on
CASSANDRA-9037 (and so transitively on
https://datastax-oss.atlassian.net/browse/JAVA-700).
These changes touch a lot of classes, but in most cases they're rather trivial.
Aside from the basic machinery around the {{IResource}} hierarchy and
granting/revoking of permissions, the bulk of the changes pertain to two things:
* Identifying all of the functions involved in the execution of a CQL statement
* Passing some context information into the preparation step which transforms
raw terms into their prepared equivalents
The former is to enable us to check at execution time that the logged in user
has sufficient permissions to execute a given statement. As there are many
places in the class hierarchy of {{SelectStatement}} and
{{ModificationStatement}} where a {{FunctionCall}} might be, I added a method
{{Iterable<Function> getFunctions()}} to a number of classes & interfaces. The
result is that at the top level, in {{checkAccess()}}, we can extract a list of
all the functions involved in the execution and check the user's permissions on
each of them. I took guidance from the existing {{usesFunction}} method, which
determines prepared statements that need to be removed from cache when a
function is dropped. Also, I used the code coverage check to verify that I'd
hit all the places where a function might be used, but it is possible that I
missed some.
Not all functions get evaluated at execution time however. A pure function with
only terminal arguments is executed at prepare time, hence the addition of the
{{PreparationContext}} argument to {{Term.Raw#prepare}}. This is (currently)
just a wrapper around a {{ClientState}} instance which enables us to do the
permissions check before executing terminal functions (in
{{FunctionCall.Raw#execute}}). The unfortunate effect of this is that it
cascades down to every {{Term.Raw}} implementation, most of which currently do
nothing with it. The only alternative I could come up with was to defer
execution of terminal functions depending on the configured {{IAuthorizer}},
but tbh that seemed worse.
h4.Permissions
This introduces a new {{EXECUTE}} permission, which is fairly self explanatory.
Aside from the obvious though, {{EXECUTE}} is also required on a scalar
function when defining it as either the state or final function in an
aggregate. So to run
{code}
CREATE AGGREGATE ks.aggregate_func(int)
SFUNC state_func
STYPE int
FINALFUNCTION final_func
INITCOND 0
{code}
would require {{EXECUTE}} on both {{state_func}} and {{final_func}}.
In addition, any user who actually run a statement using {{aggregate_func}}
will require {{EXECUTE}} on {{state_func}}, {{final_func}} and
{{aggregate_func}}.
The exception to this is that native functions, those in the {{system}}
keyspace do not require {{EXECUTE}}. So {{token()}}, {{now()}}, {{uuid}} and
{{timeuuid}} functions, plus the various type conversion functions
{{Xasblob()}} and {{blobasX()}} remain available by all users.
h4.FunctionResource
In order to apply permissions to UDFs and Aggregates, I've added a new
{{IResource}} implementation, {{FunctionResource}}
The hierarchy for {{FunctionResource}} looks like:
{code}
<all functions> - root level resource representing all
functions defined across every keyspace
<all functions in ks> - keyspace level resource to apply
permissions to all functions within a keyspace
<function ks.function(arg_types)> - a specific function, scoped to a specific
keyspace
{code}
The Collection types support {{CREATE}}, {{ALTER}}, {{DROP}}, {{AUTHORIZE}} and
{{EXECUTE}} permissions, while individual functions support
{{ALTER}}, {{DROP}}, {{AUTHORIZE}} and {{EXECUTE}} (i.e. it doesn't make sense
to grant {{CREATE}} on an existing function).
The fact that we use a distinct resource hierarchy makes it possible seperate
{{CREATE TABLE|INDEX}} privileges from {{CREATE FUNCTION|AGGREGATE}}. In turn,
this has a couple of effects.
* {{CREATE}} on a keyspace (or {{ALL KEYSPACES}}) is no longer sufficient to
create new functions/aggregates.
* The perms automatically granted to creators of db objects (CASSANDRA-7216)
have been extended to include the new {{FunctionResource}} representing all
functions in that keyspace. The upshot is that if you create a keyspace, you
have rights to create functions in it (and/or grant those rights to other
roles).
h4.Syntax
{code:title=Grant/Revoke permissions on a specific function}
GRANT EXECUTE ON test_ks.some_func(int, int, text) TO my_role
REVOKE EXECUTE ON test_ks.some_func(int, int, text) FROM my_role
{code}
{code:title=Grant/Revoke permissions on all functions in a specific keyspace}
GRANT EXECUTE ON ALL FUNCTIONS IN KEYSPACE test_ks TO my_role
REVOKE EXECUTE ON ALL FUNCTIONS IN KEYSPACE test_ks FROM my_role
{code}
{code:title=Grant/Revoke permissions on all functions across all keyspaces
(excluding system)}
GRANT EXECUTE ON ALL FUNCTIONS TO my_role
REVOKE EXECUTE ON ALL FUNCTIONS FROM my_role
{code}
h4.Tests
There is some degree of duplication between the 2 unit tests included in the
patch, however I think it can be justified.
{{UFIdentificationTest}} only checks that collection of {{Function}} objects
returned by {{CQLStatement.getFunction}} matches expectations. This is intended
to verify that the various subcomponents of the statement ({{Operation}},
{{ColumnCondition}}, {{Term}}, {{Restriction}}, {{RestrictionSet}},
{{Selection}}, {{Selector}}, {{SelectorFactory}} etc) properly report any
functions they use. As mentioned above, purely terminal functions are resolved
& executed during preparation, so those are not included in the output of
{{getFunctions}} and {{UFAuthTest}} includes tests which do exercise this path.
tl;dr it's quicker and simpler to add tests for the various permutations to
{{UFIdentificationTest}}, but it is less comprehensive.
I've also added higher level dtests in
[this|https://github.com/beobal/cassandra-dtest/tree/7557] branch. Those are
all passing (along with the existing auth & cql tests), but I want to also
refactor {{auth_test.py}} and {{auth_roles_test.py}}, so I'll open a PR when
I've done that.
> User permissions for UDFs
> -------------------------
>
> Key: CASSANDRA-7557
> URL: https://issues.apache.org/jira/browse/CASSANDRA-7557
> Project: Cassandra
> Issue Type: Sub-task
> Components: Core
> Reporter: Tyler Hobbs
> Assignee: Sam Tunnicliffe
> Labels: client-impacting, cql, udf
> Fix For: 3.0
>
>
> We probably want some new permissions for user defined functions. Most
> RDBMSes split function permissions roughly into {{EXECUTE}} and
> {{CREATE}}/{{ALTER}}/{{DROP}} permissions.
--
This message was sent by Atlassian JIRA
(v6.3.4#6332)