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

authorisation - extract functions #461

Merged
merged 3 commits into from
Sep 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
# Unreleased

## New Features
* authorisation - adds `check_permissions` and `has_permission` functions
https://github.com/anvilistas/anvil-extras/issues/382
* `routing.lazy_route` - allows you to lazily load Forms whilst using routing
https://github.com/anvilistas/anvil-extras/pull/442
* Autocompletion: adds filter_mode property - either contains or startswith
Expand Down
23 changes: 21 additions & 2 deletions docs/guides/modules/authorisation.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,5 +61,24 @@ function is called and raises an error if not:
You can pass either a single string or a list of strings to the decorator. The function
will only be called if the logged in user has ALL the permissions listed.

Notes:
* The order of the decorators matters. `anvil.server.callable` must come before either of the authorisation module decorators.

API
---

.. function:: authentication_required(fn)

Use as a decorator for any server function that requires a logged in user

.. function:: authorisation_required(permissions)

Use as a decorator above a server function
permissions should be a string or iterable of strings


.. function:: has_permission(permissions)

Returns True/False on whether a user is logged in and has valid permissions

.. function:: check_permissions(permissions)

Raises a ValueError if there is no user or the user does not have valid permissions
58 changes: 36 additions & 22 deletions server_code/authorisation.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,34 +24,48 @@ def wrapper(*args, **kwargs):
return wrapper


def has_permission(permissions):
"""Returns True/False depending on whether a user has permission or not"""
user = anvil.users.get_user()
if user is None:
return False

if isinstance(permissions, str):
required_permissions = set([permissions])
else:
required_permissions = set(permissions)

try:
user_permissions = set(
permission["name"]
for role in user["roles"]
for permission in role["permissions"]
)
except TypeError:
return False

return required_permissions.issubset(user_permissions)


def check_permissions(permissions):
"""Checks a users permissions, raises ValueError if user does not have permissions"""
if has_permission(permissions):
return

user = anvil.users.get_user()
fail = "Authentication" if user is None else "Authorisation"

raise ValueError(f"{fail} required")


def authorisation_required(permissions):
"""A decorator to ensure a user has sufficient permissions to call a server function"""

def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
user = anvil.users.get_user()
if user is None:
raise ValueError("Authentication required")
if isinstance(permissions, str):
required_permissions = set([permissions])
else:
required_permissions = set(permissions)
try:
user_permissions = set(
[
permission["name"]
for role in user["roles"]
for permission in role["permissions"]
]
)
except TypeError:
raise ValueError("Authorisation required")

if not required_permissions.issubset(user_permissions):
raise ValueError("Authorisation required")
else:
return func(*args, **kwargs)
check_permissions(permissions)
return func(*args, **kwargs)

return wrapper

Expand Down
Loading