There are lots of ways to handle permissions (This example being one), so I thought I’d share a very simple solution I’ve used in a couple of places:
Create a permissions
table with a user
column linked to the Users
table and a True/False
column for each permission you want:
Create auth_required
and permission_required
decorators. I tend to do that in a separate decorators
server module:
import functools
import anvil.users
from anvil.tables import app_tables
def auth_required(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
if not anvil.users.get_user():
raise ValueError("Authentication required")
else:
return func(*args, **kwargs)
return wrapped
def permission_required(permissions):
def permission_required_decorator(func):
@functools.wraps(func)
def wrapped(*args, **kwargs):
user = anvil.users.get_user()
# Handle the fact that permissions might be a string or a list
if isinstance(permissions, str):
all_permissions = [permissions]
else:
all_permissions = permissions
# Get the relevant data table record or create one if it doesn't exist
permissions_row = (app_tables.permissions.get(user=user)) or (
app_tables.permissions.add_row(user=user)
)
# Raise an error if any of the required permissions is missing
has_permission = all(
[permissions_row[permission] for permission in all_permissions]
)
if not has_permission:
raise ValueError("Permission required")
else:
return func(*args, **kwargs)
return wrapped
return permission_required_decorator
Finally, use the decorators on any server function where permission is required. You can pass it a single permission or a list if more than one is necessary (using the same names as the True/False
columns in your permissions
table):
import anvil.server
from decorators import permission_required
@anvil.server.callable
@permission_required("access_ui")
@auth_required
def some_function:
do_something()
@anvil.server.callable
@permission_required(["access_ui", "edit_redacted_things"])
@auth_required
def some_other_function:
do_something_else()
The order of the decorators mattters here - first check that the user exists and is authorised, then check that it has the necessary permissions and only then make the function callable.