From 939f6e2354b8614894b2250337f151dd0a38f309 Mon Sep 17 00:00:00 2001 From: stu Date: Sun, 17 Sep 2023 23:05:31 +0800 Subject: [PATCH 1/3] authorisation - extract functions --- server_code/authorisation.py | 58 ++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 22 deletions(-) diff --git a/server_code/authorisation.py b/server_code/authorisation.py index d1e4c14b..7e750e00 100644 --- a/server_code/authorisation.py +++ b/server_code/authorisation.py @@ -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 From e6290ae2711dedd24ab6e57fd95ee056b34f29f9 Mon Sep 17 00:00:00 2001 From: stu Date: Sun, 17 Sep 2023 23:11:43 +0800 Subject: [PATCH 2/3] authorisation - add docs --- docs/guides/modules/authorisation.rst | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/docs/guides/modules/authorisation.rst b/docs/guides/modules/authorisation.rst index 5b3ee393..081bd2b4 100644 --- a/docs/guides/modules/authorisation.rst +++ b/docs/guides/modules/authorisation.rst @@ -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 From f101031bb0109971673741ebf1d46d28c80eb495 Mon Sep 17 00:00:00 2001 From: stu Date: Sun, 17 Sep 2023 23:11:48 +0800 Subject: [PATCH 3/3] Update change log --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5524a40b..7e56fd14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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