/ server_code / authorisation.py
authorisation.py
 1  # SPDX-License-Identifier: MIT
 2  #
 3  # Copyright (c) 2021 The Anvil Extras project team members listed at
 4  # https://github.com/anvilistas/anvil-extras/graphs/contributors
 5  #
 6  # This software is published at https://github.com/anvilistas/anvil-extras
 7  import functools
 8  from operator import itemgetter
 9  
10  import anvil.users
11  from anvil.tables import app_tables
12  
13  __version__ = "3.1.0"
14  
15  config = {"get_roles": itemgetter("roles")}
16  
17  
18  def set_config(**kwargs):
19      if "get_roles" in kwargs:
20          _set_user_roles_getter(kwargs["get_roles"])
21  
22  
23  def _get_roles_from_table(table_name, user):
24      return getattr(app_tables, table_name).get(user=user)["roles"]
25  
26  
27  def _set_user_roles_getter(option):
28      if option is None:
29          config["get_roles"] = itemgetter("roles")
30      elif isinstance(option, str):  # table name
31          config["get_roles"] = functools.partial(_get_roles_from_table, option)
32      else:
33          raise TypeError("get_roles: option is not valid.")
34  
35  
36  def authentication_required(func):
37      """A decorator to ensure only a valid user can call a server function"""
38  
39      @functools.wraps(func)
40      def wrapper(*args, **kwargs):
41          if anvil.users.get_user() is None:
42              raise ValueError("Authentication required")
43          else:
44              return func(*args, **kwargs)
45  
46      return wrapper
47  
48  
49  def has_permission(permissions):
50      """Returns True/False depending on whether a user has permission or not"""
51      user = anvil.users.get_user()
52      if user is None:
53          return False
54  
55      if isinstance(permissions, str):
56          required_permissions = set([permissions])
57      else:
58          required_permissions = set(permissions)
59  
60      try:
61          user_permissions = set(
62              permission["name"]
63              for role in config["get_roles"](user)
64              for permission in role["permissions"]
65          )
66      except TypeError:
67          return False
68  
69      return required_permissions.issubset(user_permissions)
70  
71  
72  def check_permissions(permissions):
73      """Checks a users permissions, raises ValueError if user does not have permissions"""
74      if has_permission(permissions):
75          return
76  
77      user = anvil.users.get_user()
78      fail = "Authentication" if user is None else "Authorisation"
79  
80      raise ValueError(f"{fail} required")
81  
82  
83  def authorisation_required(permissions):
84      """A decorator to ensure a user has sufficient permissions to call a server function"""
85  
86      def decorator(func):
87          @functools.wraps(func)
88          def wrapper(*args, **kwargs):
89              check_permissions(permissions)
90              return func(*args, **kwargs)
91  
92          return wrapper
93  
94      return decorator