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

Add readOnly access level to file-based system access control #1153

Merged

Conversation

bill-warshaw
Copy link
Member

@bill-warshaw bill-warshaw commented Jul 21, 2019

  • add a read-only field to the file-based system access control mechanism
  • optional, defaults to false if not specified
  • first rule which has read-only field explicitly set will be honored, from top to bottom

https://prestosql.io/docs/current/security/built-in-system-access-control.html

@cla-bot cla-bot bot added the cla-signed label Jul 21, 2019
Copy link
Member

@findepi findepi left a comment

Choose a reason for hiding this comment

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

@bill-warshaw this is nice. Some early feedback.

private final Optional<Pattern> userRegex;
private final Optional<Pattern> catalogRegex;

@JsonCreator
public CatalogAccessControlRule(
@JsonProperty("allow") boolean allow,
@JsonProperty("readOnly") boolean readOnly,
Copy link
Member

Choose a reason for hiding this comment

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

Can we think of any other access modes in the future? If yes, this would be an enum.

Then in JSON this could look like:

   "access": "READ_ONLY",

Copy link
Member

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

I didn't come up with any future extensions so in JSON this may be represented as "readOnly": true (as implemented), but maybe @electrum has some other thoughts.

@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch 2 times, most recently from a3f4469 to 536c3eb Compare July 21, 2019 22:16
Copy link
Member

@findepi findepi left a comment

Choose a reason for hiding this comment

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

@bill-warshaw thanks for updating the code and sorry for the review delay.
A couple of editorial comments.

private final Optional<Pattern> userRegex;
private final Optional<Pattern> catalogRegex;

@JsonCreator
public CatalogAccessControlRule(
@JsonProperty("allow") boolean allow,
@JsonProperty("readOnly") boolean readOnly,
Copy link
Member

Choose a reason for hiding this comment

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

I didn't come up with any future extensions so in JSON this may be represented as "readOnly": true (as implemented), but maybe @electrum has some other thoughts.

@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch 3 times, most recently from fcf2d39 to cb3d97a Compare August 5, 2019 21:33
@bill-warshaw
Copy link
Member Author

@findepi comments addressed, and this passes tests for me locally. Thanks for the review!

Should I also update the docs as well? Or is there a more formal process for doc updates?

@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch from cb3d97a to da0f528 Compare August 6, 2019 03:48
@electrum
Copy link
Member

electrum commented Aug 6, 2019

Yes, please update the docs as part of the same commit (in the presto-docs module).

@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch from da0f528 to 431e5b3 Compare August 6, 2019 15:39
@bill-warshaw
Copy link
Member Author

initial docs added, also renamed it to read-only to be consistent with the existing read-only system-level access control mechanism. Open to feedback on the docs

@findepi findepi requested a review from electrum August 6, 2019 20:33
@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch 2 times, most recently from 4d3136a to 047d4bf Compare August 14, 2019 21:48
@dain dain self-assigned this Aug 16, 2019
@dain dain requested review from dain and removed request for electrum August 16, 2019 23:15
@dain
Copy link
Member

dain commented Aug 26, 2019

@findepi @electrum @martint: for this PR we are introducing a "read-only" concept to the file based access control. This is in addition to the existing allow-all/deny-all we currently have. Should we instead go down the path of specifying actual GRANT actions like SELECT, INSERT, DELETE?

Copy link
Member

@dain dain left a comment

Choose a reason for hiding this comment

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

This change looks good to me. I'd like to hear from others on the approach before merging.

@JsonProperty("user") Optional<Pattern> userRegex,
@JsonProperty("catalog") Optional<Pattern> catalogRegex)
{
this.allow = allow;
this.readOnly = readOnly;
Copy link
Member

Choose a reason for hiding this comment

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

Should we enforce that readOnly can only be set when allow is set?

Copy link
Member

Choose a reason for hiding this comment

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

Make sense. Or we could turn allow into a tri-state flag "allow" | "deny" | "read-only" with true | false as legacy options.

Copy link
Member

Choose a reason for hiding this comment

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

I like the thought process, but these names look strange:

"allow": "allow"
"allow": "deny"

Maybe "all" | "read-only" | "none"

Or keep true/false and add read-only as the third option. (having multiple values for the same state is easy with an enum)

Copy link
Member

Choose a reason for hiding this comment

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

If we want different names than booleans, we could do something like

enum AccessMode
{
    ALL("all", "true"),
    READ_ONLY("read-only"),
    NONE("none", "false"),

    ...
}

Copy link
Member

Choose a reason for hiding this comment

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

That would be great! And, as I understand, you need something like

@JsonCreator
public static AccessMode fromString(String value) { ... }

right?

@findepi
Copy link
Member

findepi commented Aug 26, 2019

for this PR we are introducing a "read-only" concept to the file based access control. This is in addition to the existing allow-all/deny-all we currently have. Should we instead go down the path of specifying actual GRANT actions like SELECT, INSERT, DELETE?

Since this can be managed by hand, I prefer a simplified model (no access | full access | read-only).

@JsonProperty("user") Optional<Pattern> userRegex,
@JsonProperty("catalog") Optional<Pattern> catalogRegex)
{
this.allow = allow;
this.readOnly = readOnly;
Copy link
Member

Choose a reason for hiding this comment

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

I like the thought process, but these names look strange:

"allow": "allow"
"allow": "deny"

Maybe "all" | "read-only" | "none"

Or keep true/false and add read-only as the third option. (having multiple values for the same state is easy with an enum)

@JsonProperty("user") Optional<Pattern> userRegex,
@JsonProperty("catalog") Optional<Pattern> catalogRegex)
{
this.allow = allow;
this.readOnly = readOnly;
Copy link
Member

Choose a reason for hiding this comment

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

If we want different names than booleans, we could do something like

enum AccessMode
{
    ALL("all", "true"),
    READ_ONLY("read-only"),
    NONE("none", "false"),

    ...
}

@dain
Copy link
Member

dain commented Aug 27, 2019

@bill-warshaw What do you think about @electrum's suggestion to change allow to "all" | "read-only" | "none"? If you do, do you have time to make that change? If not, I can make the change.

@bill-warshaw
Copy link
Member Author

@dain I would be open to it, but I'm not entirely sure how to do it cleanly. We'd end up with a JSON field that could be of either type boolean or string, which wouldn't map nicely to a POJO, since we have to support the following values:

true
false
"all"
"read-only"
"none"

Is there an easy way to model this kind of union type with Jackson annotations?

@dain
Copy link
Member

dain commented Aug 27, 2019

Edit: fixed it to support real booleans :)

@bill-warshaw This should do it: https://gist.github.com/dain/720382a08898e4c54f80ff80bdd8b9d3

@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch from 047d4bf to 786a748 Compare August 28, 2019 01:18
@bill-warshaw
Copy link
Member Author

bill-warshaw commented Aug 28, 2019

@electrum @dain comments addressed, I used @dain 's code snippet and it definitely feels cleaner.

@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch from 786a748 to 74b7432 Compare August 28, 2019 01:20
Copy link
Member

@findepi findepi left a comment

Choose a reason for hiding this comment

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

(not full review; i didn't review tests,docs)

@findepi
Copy link
Member

findepi commented Aug 28, 2019

@bill-warshaw FYI TestFileBasedSystemAccessControl failed on Travis

[ERROR] testCatalogOperations(io.prestosql.plugin.base.security.TestFileBasedSystemAccessControl)  Time elapsed: 0.027 s  <<< FAILURE!
java.lang.IllegalArgumentException: Invalid JSON file '/home/travis/build/prestosql/presto/presto-plugin-toolkit/target/test-classes/catalog.json' for 'class io.prestosql.plugin.base.security.FileBasedSystemAccessControlRules'
	at io.prestosql.plugin.base.JsonUtils.parseJson(JsonUtils.java:49)
	at io.prestosql.plugin.base.security.FileBasedSystemAccessControl$Factory.create(FileBasedSystemAccessControl.java:134)
	at io.prestosql.plugin.base.security.FileBasedSystemAccessControl$Factory.create(FileBasedSystemAccessControl.java:122)
	at io.prestosql.plugin.base.security.TestFileBasedSystemAccessControl.newFileBasedSystemAccessControl(TestFileBasedSystemAccessControl.java:168)
	at io.prestosql.plugin.base.security.TestFileBasedSystemAccessControl.newFileBasedSystemAccessControl(TestFileBasedSystemAccessControl.java:163)
	at io.prestosql.plugin.base.security.TestFileBasedSystemAccessControl.testCatalogOperations(TestFileBasedSystemAccessControl.java:102)

(+2 other fails in the class)

@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch from 74b7432 to 905f770 Compare August 28, 2019 15:14
@bill-warshaw
Copy link
Member Author

@findepi comments addressed, open question on whether or not allow being documented as required but not actually required in code is behavior we want to maintain.

Tests passed locally for me with the most recent changes. I thought they worked locally the last time I pushed but I've had some weirdness with my local build so I might have been running against stale code.

Copy link
Member

@findepi findepi left a comment

Choose a reason for hiding this comment

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

LGTM (not full review; i didn't review tests,docs)

@dain ?

@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch from 905f770 to 775cf45 Compare August 29, 2019 13:48
@dain
Copy link
Member

dain commented Aug 30, 2019

There is a test failure that looks related:

[ERROR]   TestFileBasedSystemAccessControl.testCanSetUserOperations:95->newFileBasedSystemAccessControl:163->newFileBasedSystemAccessControl:168 » IllegalArgument
[ERROR]   TestFileBasedSystemAccessControl.testCatalogOperations:102->newFileBasedSystemAccessControl:163->newFileBasedSystemAccessControl:168 » IllegalArgument
[ERROR]   TestFileBasedSystemAccessControl.testRefreshing:132 » IllegalArgument Invalid ...

Here is a stack:

[ERROR] testRefreshing(io.prestosql.plugin.base.security.TestFileBasedSystemAccessControl)  Time elapsed: 0.044 s  <<< FAILURE!
java.lang.IllegalArgumentException: Invalid JSON file '/tmp/a0f727ae-10b9-439d-bf1e-af4d3de397ac.txt' for 'class io.prestosql.plugin.base.security.FileBasedSystemAccessControlRules'
	at io.prestosql.plugin.base.JsonUtils.parseJson(JsonUtils.java:49)
	at io.prestosql.plugin.base.security.FileBasedSystemAccessControl$Factory.create(FileBasedSystemAccessControl.java:136)
	at io.prestosql.plugin.base.security.FileBasedSystemAccessControl$Factory.lambda$create$0(FileBasedSystemAccessControl.java:119)
	at com.google.common.base.Suppliers$ExpiringMemoizingSupplier.get(Suppliers.java:241)
	at io.prestosql.plugin.base.security.ForwardingSystemAccessControl$1.delegate(ForwardingSystemAccessControl.java:44)
	at io.prestosql.plugin.base.security.ForwardingSystemAccessControl.checkCanCreateView(ForwardingSystemAccessControl.java:192)
	at io.prestosql.plugin.base.security.TestFileBasedSystemAccessControl.testRefreshing(TestFileBasedSystemAccessControl.java:132)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.testng.internal.MethodInvocationHelper.invokeMethod(MethodInvocationHelper.java:104)
	at org.testng.internal.Invoker.invokeMethod(Invoker.java:645)
	at org.testng.internal.Invoker.invokeTestMethod(Invoker.java:851)
	at org.testng.internal.Invoker.invokeTestMethods(Invoker.java:1177)
	at org.testng.internal.TestMethodWorker.invokeTestMethods(TestMethodWorker.java:129)
	at org.testng.internal.TestMethodWorker.run(TestMethodWorker.java:112)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
	at java.lang.Thread.run(Thread.java:748)
Caused by: com.fasterxml.jackson.databind.exc.InvalidDefinitionException: Cannot construct instance of `io.prestosql.plugin.base.security.CatalogAccessControlRule`, problem: accessMode is null
 at [Source: (byte[])"{
  "catalogs": [
    {
      "user": "admin",
      "catalog": ".*",
      "allow": true
    },
    {
      "catalog": "secret",
      "allow": false
    },
    {
      "user": ".*",
      "catalog": "open-to-all",
      "allow": true
    },
    {
      "catalog": "all-allowed",
      "allow": true
    },
    {
      "user": "alice",
      "catalog": "alice-catalog",
      "allow": true
    },
    {
      "user": "bob",
      "catalog": "alice-catalog",
      "allow": false
    },
    {
      ""[truncated 147 bytes]; line: 33, column: 5] (through reference chain: io.prestosql.plugin.base.security.FileBasedSystemAccessControlRules["catalogs"]->java.util.ArrayList[6])
	at com.fasterxml.jackson.databind.exc.InvalidDefinitionException.from(InvalidDefinitionException.java:67)
	at com.fasterxml.jackson.databind.DeserializationContext.instantiationException(DeserializationContext.java:1608)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.wrapAsJsonMappingException(StdValueInstantiator.java:484)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.rewrapCtorProblem(StdValueInstantiator.java:503)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:285)
	at com.fasterxml.jackson.databind.deser.ValueInstantiator.createFromObjectWith(ValueInstantiator.java:229)
	at com.fasterxml.jackson.databind.deser.impl.PropertyBasedCreator.build(PropertyBasedCreator.java:195)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:488)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1287)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:286)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:245)
	at com.fasterxml.jackson.databind.deser.std.CollectionDeserializer.deserialize(CollectionDeserializer.java:27)
	at com.fasterxml.jackson.databind.deser.std.ReferenceTypeDeserializer.deserialize(ReferenceTypeDeserializer.java:185)
	at com.fasterxml.jackson.databind.deser.SettableBeanProperty.deserialize(SettableBeanProperty.java:530)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeWithErrorWrapping(BeanDeserializer.java:528)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer._deserializeUsingPropertyBased(BeanDeserializer.java:417)
	at com.fasterxml.jackson.databind.deser.BeanDeserializerBase.deserializeFromObjectUsingNonDefault(BeanDeserializerBase.java:1287)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserializeFromObject(BeanDeserializer.java:326)
	at com.fasterxml.jackson.databind.deser.BeanDeserializer.deserialize(BeanDeserializer.java:159)
	at com.fasterxml.jackson.databind.ObjectMapper._readMapAndClose(ObjectMapper.java:4013)
	at com.fasterxml.jackson.databind.ObjectMapper.readValue(ObjectMapper.java:3091)
	at io.prestosql.plugin.base.JsonUtils.parseJson(JsonUtils.java:46)
	... 19 more
Caused by: java.lang.NullPointerException: accessMode is null
	at java.util.Objects.requireNonNull(Objects.java:228)
	at io.prestosql.plugin.base.security.CatalogAccessControlRule.<init>(CatalogAccessControlRule.java:41)
	at sun.reflect.GeneratedConstructorAccessor5.newInstance(Unknown Source)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.fasterxml.jackson.databind.introspect.AnnotatedConstructor.call(AnnotatedConstructor.java:124)
	at com.fasterxml.jackson.databind.deser.std.StdValueInstantiator.createFromObjectWith(StdValueInstantiator.java:283)
	... 38 more

@bill-warshaw
Copy link
Member Author

bill-warshaw commented Sep 2, 2019

@dain I'm working on reproducing this locally, but all of the tests under TestFileBasedSystemAccessControl pass locally for me, both from Maven and from IntelliJ (on OSX).

I could have sworn I saw all TestFileBasedSystemAccessControl pass at least once on Travis recently, but there are multiple recent runs where these tests fail so something's clearly off here.

I'll post again once I figure it out.

@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch from 775cf45 to 27ec530 Compare September 2, 2019 23:10
@bill-warshaw
Copy link
Member Author

@dain figured it out - there are two test classes named TestFileBasedSystemAccessControl which both exercise file-based system access control, one in presto-main, and one in presto-plugin-toolkit. Both share some logic and have similar resource files, but also appear to have diverged before this PR.

To fix these tests I just updated one of the resource files under presto-plugin-toolkit to remove the catalog which did not have the allow value set; should we try to merge these two test classes instead?

@findepi
Copy link
Member

findepi commented Sep 3, 2019

there are two test classes named TestFileBasedSystemAccessControl which both exercise file-based system access control, one in presto-main, and one in presto-plugin-toolkit. Both share some logic and have similar resource files, but also appear to have diverged before this PR.

@kokosing can you please comment?

should we try to merge these two test classes instead?

even if we decide to merge the classes, I'd rather have it in a separate (follow-up) PR

@kokosing
Copy link
Member

kokosing commented Sep 3, 2019

should we try to merge these two test classes instead?

Please don't. The test file based access control on two different levels. Directly and via access control manager. There is a contract between these two, like access control manager is not going to ask to access control for access to a table if it is known that there is no access to schema to which given table belongs.


@JsonValue
@Override
public String toString()
Copy link
Member

Choose a reason for hiding this comment

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

I'd prefer not to override toString() (nor name()) and have a dedicated method for this. Maybe getStringValue().

@electrum WDYT?

* adds a `read-only` option to the file-based system access control
  mechanism
* three roles exist for `allow`: `all`, `read-only`, `none`
* legacy `allow` values of `true | false` map to `all` and `none`
* `allow` can be set to `all | read-only | none`
@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch from 27ec530 to ab6001c Compare September 6, 2019 00:05
* create, rename and drop schema operations can
  now be controlled by the file-based SystemAccessControl
  mechanism
* enabled when allow is set to `all`, disabled when `allow`
  is set to `none` or `read-only`
@bill-warshaw bill-warshaw force-pushed the file_based_read_only_access_control branch from ab6001c to 2f02151 Compare September 6, 2019 00:06
@findepi findepi merged commit 78974ab into trinodb:master Sep 6, 2019
@findepi
Copy link
Member

findepi commented Sep 6, 2019

Merged, thanks!

@findepi findepi mentioned this pull request Sep 6, 2019
6 tasks
@bill-warshaw bill-warshaw deleted the file_based_read_only_access_control branch September 6, 2019 14:26
@martint martint added this to the 319 milestone Sep 20, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

Successfully merging this pull request may close these issues.

None yet

6 participants