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

Having view-table permission but NOT view-database should still grant access to /db/table #832

Closed
simonw opened this issue Jun 11, 2020 · 12 comments

Comments

@simonw
Copy link
Owner

simonw commented Jun 11, 2020

Stumbled into this while working on datasette-permissions-sql. I had granted table permissions, but the permission check wasn't even executed because the user failed the previous view-database check.

@simonw
Copy link
Owner Author

simonw commented Jun 11, 2020

Relevant code:

if not is_view and not table_exists:
raise NotFound("Table not found: {}".format(table))
await self.check_permission(request, "view-instance")
await self.check_permission(request, "view-database", database)
await self.check_permission(request, "view-table", (database, table))

@simonw
Copy link
Owner Author

simonw commented Jun 11, 2020

May the fix here is to implement a .check_permissions() method which passes when the first permission passes?

await self.check_permissions(request, [
    ("view-table", (database, table)),
    ("view-database", database),
    "view-instance",
])

@simonw
Copy link
Owner Author

simonw commented Jun 11, 2020

How would I document this? Probably in another section on https://datasette.readthedocs.io/en/latest/authentication.html#permissions

But I'd also need to add documentation to the individual views stating what permissions are checked and in what order. I could do that on this page: https://datasette.readthedocs.io/en/latest/pages.html

@simonw simonw changed the title If you have view-table permission and NOT view-database, should you be able to see tables? Having view-table permission but NOT view-database should still grant access to /db/table Jun 11, 2020
@simonw
Copy link
Owner Author

simonw commented Jun 11, 2020

So for the following:

await self.check_permissions(request, [
    ("view-table", (database, table)),
    ("view-database", database),
    "view-instance",
])

The logic is: if the first test returns True, you get access. If it returns False you are denied. If it says None then move on to the next check in the list and repeat.

@simonw
Copy link
Owner Author

simonw commented Jun 11, 2020

I think the new .check_permissions() should be a documented utility that is available to plugins.
Maybe a method on datasette?

@simonw simonw modified the milestones: Datasette 0.44, Datasette 0.45 Jun 11, 2020
@simonw simonw pinned this issue Jun 30, 2020
@simonw
Copy link
Owner Author

simonw commented Jun 30, 2020

I already have this method on Datasette:

async def permission_allowed(self, actor, action, resource=None, default=False):

What would be a good method name that complements that and indicates "check a list of permissions in order"? Should it even run against the request or should you have to hand it request.actor?

@simonw
Copy link
Owner Author

simonw commented Jun 30, 2020

I could rename permission_allowed() to check_permission() and have a complementary check_permissions() method.

This is a breaking change but we're pre-1.0 so I think that's OK. I could even set up a temporary permission_allowed() alias which prints a deprecation warning to the console, then remove that at 1.0.

@simonw
Copy link
Owner Author

simonw commented Jun 30, 2020

permission_allowed is already the name of the pugin hook. It's actually a bit confusing that it's also the name of a method on datasette..

@simonw
Copy link
Owner Author

simonw commented Jun 30, 2020

Hah... but check_permissionis a method onBaseView`. Here are the various permission methods at the moment:

@hookimpl(tryfirst=True)
def permission_allowed(datasette, actor, action, resource):
async def inner():
if action == "permissions-debug":
if actor and actor.get("id") == "root":
return True
elif action == "view-instance":
allow = datasette.metadata("allow")
if allow is not None:
return actor_matches_allow(actor, allow)

And on BaseView:

async def check_permission(self, request, action, resource=None):
ok = await self.ds.permission_allowed(
request.actor, action, resource=resource, default=True,
)
if not ok:
raise Forbidden(action)

@simonw
Copy link
Owner Author

simonw commented Jun 30, 2020

I'm going to put the new check_permissions() method on BaseView as well. If I want that method to be available to plugins I can do so by turning that BaseView class into a documented API that plugins are encouraged to use themselves.

@simonw
Copy link
Owner Author

simonw commented Jun 30, 2020

Tests needed for this:

  • If a user has view table but NOT view database / view instance, can they view the table page?
  • If a user has view canned query but NOT view database / view instance, can they view the canned query page?
  • If a user has view database but NOT view instance, can they view the database page?

@simonw
Copy link
Owner Author

simonw commented Jun 30, 2020

I don't think this needs any additional documentation - the new behaviour matches how the permissions are documented here: https://datasette.readthedocs.io/en/0.44/authentication.html#built-in-permissions

simonw added a commit that referenced this issue Jun 30, 2020
Default permission policy was returning True by default for permission
checks - which means that if allow was not defined for a level it would
be treated as a passing check.

This is better: we now return None of the allow block is not defined,
which means 'I have no opinion on this' and allows other code to make
its own decisions.

Added while working on #832
@simonw simonw closed this as completed in d6e03b0 Jun 30, 2020
simonw added a commit that referenced this issue Jul 1, 2020
@simonw simonw unpinned this issue Jul 1, 2020
simonw added a commit that referenced this issue Jul 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant