5aef823807
- Removed the restricted data cache (it wasn't used since OS 3.0) - unify functions for restricted and full data: Just one function, which accteps an optional user_id: If it is None, full data is returned, and with a user id given, the restricted data - More atomic access to redis, especially for: - Check for data-existance in redis and do an auto-ensure-cache. - Speedup through hashing of scripts and redis' script cache. - Save schema version into the redis cache and rebuild, if the version changed Client changes: - Simplified the ConstantsService - Fixed bug, when receiving an autoupdate with all_data=True from the Server
103 lines
3.5 KiB
Python
103 lines
3.5 KiB
Python
from typing import Any, Callable, Dict, List, Set
|
|
|
|
from asgiref.sync import async_to_sync
|
|
|
|
from .auth import async_anonymous_is_enabled, async_has_perm, user_to_user_id
|
|
from .cache import element_cache
|
|
|
|
|
|
class BaseAccessPermissions:
|
|
"""
|
|
Base access permissions container.
|
|
|
|
Every app which has autoupdate models has to create classes subclassing
|
|
from this base class for every autoupdate root model.
|
|
"""
|
|
|
|
base_permission = ""
|
|
"""
|
|
Set to a permission the user needs to see the element.
|
|
|
|
If this string is empty, all users can see it.
|
|
"""
|
|
|
|
def check_permissions(self, user_id: int) -> bool:
|
|
"""
|
|
Returns True if the user has read access to model instances.
|
|
"""
|
|
# Convert user to right type
|
|
# TODO: Remove this and make sure, that user has always the right type
|
|
user_id = user_to_user_id(user_id)
|
|
return async_to_sync(self.async_check_permissions)(user_id)
|
|
|
|
async def async_check_permissions(self, user_id: int) -> bool:
|
|
"""
|
|
Returns True if the user has read access to model instances.
|
|
"""
|
|
if self.base_permission:
|
|
return await async_has_perm(user_id, self.base_permission)
|
|
else:
|
|
return bool(user_id) or await async_anonymous_is_enabled()
|
|
|
|
async def get_restricted_data(
|
|
self, full_data: List[Dict[str, Any]], user_id: int
|
|
) -> List[Dict[str, Any]]:
|
|
"""
|
|
Returns the restricted serialized data for the instance prepared
|
|
for the user.
|
|
|
|
The argument full_data has to be a list of full_data dicts. The type of
|
|
the return is the same. Returns an empty list if the user has no read
|
|
access. Returns reduced data if the user has limited access. Default:
|
|
Returns full data if the user has read access to model instances.
|
|
"""
|
|
return full_data if await self.async_check_permissions(user_id) else []
|
|
|
|
|
|
class RequiredUsers:
|
|
"""
|
|
Helper class to find all users that are required by another element.
|
|
"""
|
|
|
|
callables: Dict[str, Callable[[Dict[str, Any]], Set[int]]] = {}
|
|
|
|
def get_collection_strings(self) -> Set[str]:
|
|
"""
|
|
Returns all collection strings for elements that could have required users.
|
|
"""
|
|
return set(self.callables.keys())
|
|
|
|
def add_collection_string(
|
|
self, collection_string: str, callable: Callable[[Dict[str, Any]], Set[int]]
|
|
) -> None:
|
|
"""
|
|
Add a callable for a collection_string to get the required users of the
|
|
elements.
|
|
"""
|
|
self.callables[collection_string] = callable
|
|
|
|
async def get_required_users(self, collection_strings: Set[str]) -> Set[int]:
|
|
"""
|
|
Returns the user ids that are required by other elements.
|
|
|
|
Returns only user ids required by elements with a collection_string
|
|
in the argument collection_strings.
|
|
"""
|
|
user_ids: Set[int] = set()
|
|
|
|
for collection_string in collection_strings:
|
|
collection_data = await element_cache.get_collection_data(collection_string)
|
|
# Get the callable for the collection_string
|
|
get_user_ids = self.callables.get(collection_string)
|
|
if not (get_user_ids and collection_data):
|
|
# if the collection_string is unknown or it has no data, do nothing
|
|
continue
|
|
|
|
for element in collection_data.values():
|
|
user_ids.update(get_user_ids(element))
|
|
|
|
return user_ids
|
|
|
|
|
|
required_user = RequiredUsers()
|