sharing: replace type bday by conversion
This commit is contained in:
@@ -19,6 +19,7 @@ import base64
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import os # TODO: remove/3.7.0-final
|
||||
import re
|
||||
import socket
|
||||
import uuid
|
||||
@@ -34,7 +35,7 @@ from radicale.log import logger
|
||||
|
||||
INTERNAL_TYPES: Sequence[str] = ("csv", "files", "none")
|
||||
|
||||
DB_FIELDS_V1: Sequence[str] = ('ShareType', 'PathOrToken', 'PathMapped', 'Owner', 'User', 'Permissions', 'EnabledByOwner', 'EnabledByUser', 'HiddenByOwner', 'HiddenByUser', 'TimestampCreated', 'TimestampUpdated', 'Properties', 'Conversion', 'Actions')
|
||||
DB_FIELDS_V1: Sequence[str] = ('ShareType', 'PathOrToken', 'PathMapped', 'Conversion', 'Owner', 'User', 'Permissions', 'EnabledByOwner', 'EnabledByUser', 'HiddenByOwner', 'HiddenByUser', 'TimestampCreated', 'TimestampUpdated', 'Properties', 'Actions')
|
||||
# ShareType: <token|map>
|
||||
# PathOrToken: <path|token> [PrimaryKey]
|
||||
# PathMapped: <path>
|
||||
@@ -48,8 +49,8 @@ DB_FIELDS_V1: Sequence[str] = ('ShareType', 'PathOrToken', 'PathMapped', 'Owner'
|
||||
# TimestampCreated: <unixtime> (when created)
|
||||
# TimestampUpdated: <unixtime> (last update)
|
||||
# Properties: Overlay of collection properties in JSON
|
||||
# Conversion: None|bday
|
||||
# bday: check VCARD(vcf) for BDAY and convert to reoccuring VEVENT(ics)
|
||||
# Conversion: none|bday
|
||||
# bday: check VADDRESSBOOK VCARD(vcf) entries for BDAY and convert to VCALENDAR reoccuring VEVENT(ics)
|
||||
# Actions: Actions structure in JSON
|
||||
# (future reserved for e.g. "filter", "filter_pre", "filter_post" or anything else, implemented on request)
|
||||
|
||||
@@ -73,14 +74,17 @@ DB_TYPES_V1: dict[str, type] = {
|
||||
|
||||
DB_FIELDS_V1_USER_PERMITTED: Sequence[str] = ('EnabledByUser', 'HiddenByUser', 'Properties')
|
||||
|
||||
SHARE_TYPES: Sequence[str] = ('token', 'map', 'bday', 'all')
|
||||
|
||||
SHARE_TYPES_V1: Sequence[str] = ('token', 'map', 'bday')
|
||||
SHARE_TYPES: Sequence[str] = ('token', 'map', 'all')
|
||||
# token: share by secret token (does not require authentication)
|
||||
# map : share by mapping collection of one user to another as virtual
|
||||
# bday : share by mapping addressbook-collection of one user to another as virtual calendar-collection
|
||||
# all : only supported for "list" and "info"
|
||||
|
||||
SHARE_TYPES_V1: Sequence[str] = ('token', 'map')
|
||||
|
||||
if "SHARING_NO_LEGACY" not in os.environ: # TODO: remove/3.7.0-final
|
||||
SHARE_TYPES: Sequence[str] = ('token', 'map', 'bday', 'all') # type: ignore[no-redef] # TODO: remove/3.7.0-final
|
||||
SHARE_TYPES_V1: Sequence[str] = ('token', 'map', 'bday') # type: ignore[no-redef] # TODO: remove/3.7.0-final
|
||||
|
||||
API_HOOKS_V1: Sequence[str] = ('list', 'create', 'delete', 'update', 'hide', 'unhide', 'enable', 'disable', 'info')
|
||||
# list : list sharings (optional filtered)
|
||||
# create : create share by token or map
|
||||
@@ -115,6 +119,7 @@ API_TYPES_V1: dict[str, type] = {
|
||||
"Properties": dict,
|
||||
"Conversion": str,
|
||||
"Actions": dict,
|
||||
"SupportedConversions": list,
|
||||
}
|
||||
|
||||
|
||||
@@ -126,7 +131,7 @@ USER_PATTERN: str = "([a-zA-Z0-9@]+)" # TODO: extend or find better source
|
||||
|
||||
OVERLAY_PROPERTIES_WHITELIST: Sequence[str] = ("C:calendar-description", "ICAL:calendar-color", "CR:addressbook-description", "INF:addressbook-color", "D:displayname")
|
||||
|
||||
CONVERSIONS_WHITELIST: Sequence[str] = ("bday")
|
||||
CONVERSIONS_WHITELIST: Sequence[str] = ("bday", "none")
|
||||
|
||||
|
||||
def load(configuration: "config.Configuration") -> "BaseSharing":
|
||||
@@ -157,20 +162,21 @@ class BaseSharing:
|
||||
# Sharing
|
||||
self.sharing_collection_by_map = configuration.get("sharing", "collection_by_map")
|
||||
self.sharing_collection_by_token = configuration.get("sharing", "collection_by_token")
|
||||
self.sharing_collection_by_bday = configuration.get("sharing", "collection_by_bday")
|
||||
if "SHARING_NO_LEGACY" not in os.environ: # TODO: remove/3.7.0-final
|
||||
self.sharing_collection_by_map = self.sharing_collection_by_map or configuration.get("sharing", "collection_by_bday") # TODO: remove/3.7.0-final
|
||||
self.permit_create_token = configuration.get("sharing", "permit_create_token")
|
||||
self.permit_create_map = configuration.get("sharing", "permit_create_map")
|
||||
self.permit_create_bday = configuration.get("sharing", "permit_create_bday")
|
||||
if "SHARING_NO_LEGACY" not in os.environ: # TODO: remove/3.7.0-final
|
||||
self.permit_create_map = self.permit_create_map or configuration.get("sharing", "permit_create_bday") # TODO: remove/3.7.0-final
|
||||
self.default_permissions_create_token = configuration.get("sharing", "default_permissions_create_token")
|
||||
self.default_permissions_create_map = configuration.get("sharing", "default_permissions_create_map")
|
||||
self.permit_properties_overlay = configuration.get("sharing", "permit_properties_overlay")
|
||||
self.enforce_properties_overlay = configuration.get("sharing", "enforce_properties_overlay")
|
||||
|
||||
logger.info("sharing.collection_by_map : %s", self.sharing_collection_by_map)
|
||||
logger.info("sharing.collection_by_token: %s", self.sharing_collection_by_token)
|
||||
logger.info("sharing.collection_by_bday : %s", self.sharing_collection_by_bday)
|
||||
logger.info("sharing.permit_create_token: %s", self.permit_create_token)
|
||||
logger.info("sharing.permit_create_map : %s", self.permit_create_map)
|
||||
logger.info("sharing.permit_create_bday : %s", self.permit_create_bday)
|
||||
logger.info("sharing.default_permissions_create_token: %r", self.default_permissions_create_token)
|
||||
logger.info("sharing.default_permissions_create_map : %r", self.default_permissions_create_map)
|
||||
logger.info("sharing.permit_properties_overlay: %s", self.permit_properties_overlay)
|
||||
@@ -180,7 +186,7 @@ class BaseSharing:
|
||||
self.sharing_db_type = configuration.get("sharing", "type")
|
||||
logger.info("sharing.database_type: %s", self.sharing_db_type)
|
||||
|
||||
if ((self.sharing_collection_by_map is False) and (self.sharing_collection_by_token is False) and (self.sharing_collection_by_bday is False)):
|
||||
if ((self.sharing_collection_by_map is False) and (self.sharing_collection_by_token is False)):
|
||||
logger.info("sharing disabled as no feature is enabled")
|
||||
self._enabled = False
|
||||
return
|
||||
@@ -195,7 +201,7 @@ class BaseSharing:
|
||||
"""
|
||||
try:
|
||||
if self.database_init() is False:
|
||||
logger.info("sharing disabled as no database is active")
|
||||
logger.warning("sharing disabled as no database is active")
|
||||
self._enabled = False
|
||||
return False
|
||||
except Exception as e:
|
||||
@@ -247,13 +253,13 @@ class BaseSharing:
|
||||
def database_create_sharing(self,
|
||||
ShareType: str,
|
||||
PathOrToken: str, PathMapped: str,
|
||||
Conversion: str,
|
||||
Owner: str, User: str,
|
||||
Permissions: str = "r",
|
||||
EnabledByOwner: bool = False, EnabledByUser: bool = False,
|
||||
HiddenByOwner: bool = True, HiddenByUser: bool = True,
|
||||
Timestamp: int = 0,
|
||||
Properties: Union[dict, None] = None,
|
||||
Conversion: Union[str, None] = None,
|
||||
Actions: Union[dict, None] = None,
|
||||
) -> dict:
|
||||
""" create sharing """
|
||||
@@ -350,7 +356,12 @@ class BaseSharing:
|
||||
|
||||
# *** sharing functions called by request methods ***
|
||||
# list sharings
|
||||
def sharing_collection_list(self, User: Union[str, None] = None, Enabled: Union[bool, None] = None, Hidden: Union[bool, None] = None) -> list[dict]:
|
||||
def sharing_collection_list(self,
|
||||
User: Union[str, None] = None,
|
||||
Enabled: Union[bool, None] = None,
|
||||
Hidden: Union[bool, None] = None,
|
||||
Conversion: Union[str, None] = None,
|
||||
) -> list[dict]:
|
||||
""" returning dict with shared collections by filter(User/Enabled/Hidden) or None if not found"""
|
||||
sharing_collection_list = []
|
||||
|
||||
@@ -366,27 +377,16 @@ class BaseSharing:
|
||||
EnabledByOwner=Enabled,
|
||||
EnabledByUser=Enabled,
|
||||
HiddenByOwner=Hidden,
|
||||
HiddenByUser=Hidden)
|
||||
|
||||
if not self.sharing_collection_by_bday:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/bday: not active")
|
||||
else:
|
||||
# retrieve collections depending on filter
|
||||
sharing_collection_list += self.database_list_sharing(
|
||||
ShareType="bday",
|
||||
OwnerOrUser=User,
|
||||
User=User,
|
||||
EnabledByOwner=Enabled,
|
||||
EnabledByUser=Enabled,
|
||||
HiddenByOwner=Hidden,
|
||||
HiddenByUser=Hidden)
|
||||
HiddenByUser=Hidden,
|
||||
Conversion=Conversion,
|
||||
)
|
||||
|
||||
return sharing_collection_list
|
||||
|
||||
# resolves a path to a share
|
||||
def sharing_collection_resolver(self, path: str, user: str) -> Union[dict, None]:
|
||||
""" returning dict with PathMapped, Owner, Permissions or None if not found"""
|
||||
logger.debug("TRACE/sharing/resolver: lookup path=%r user=%r", path, user)
|
||||
share = None
|
||||
|
||||
if path == "/":
|
||||
@@ -396,6 +396,8 @@ class BaseSharing:
|
||||
if self.sharing_collection_by_token:
|
||||
if share is None:
|
||||
share = self.sharing_collection_by_token_resolver(path)
|
||||
if share is not None and 'error' in share:
|
||||
return None
|
||||
else:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/token: not active")
|
||||
@@ -403,17 +405,12 @@ class BaseSharing:
|
||||
if self.sharing_collection_by_map:
|
||||
if share is None:
|
||||
share = self.sharing_collection_by_map_resolver(path, user)
|
||||
if share is not None and 'error' in share:
|
||||
return None
|
||||
else:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/map: not active")
|
||||
|
||||
if self.sharing_collection_by_bday:
|
||||
if share is None:
|
||||
share = self.sharing_collection_by_bday_resolver(path, user)
|
||||
else:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/bday: not active")
|
||||
|
||||
if share is not None:
|
||||
if self.permit_properties_overlay:
|
||||
if share['Permissions'] and "p" not in share['Permissions']:
|
||||
@@ -446,31 +443,44 @@ class BaseSharing:
|
||||
|
||||
# *** internal sharing functions ***
|
||||
# resolves a token "path" to a share
|
||||
# dict: share
|
||||
# None: not supported
|
||||
# False: supported but not found
|
||||
def sharing_collection_by_token_resolver(self, path) -> Union[dict, None]:
|
||||
""" returning dict with PathMapped, Owner, Permissions or None if invalid"""
|
||||
if self.sharing_collection_by_token:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/token: check path: %r", path)
|
||||
logger.debug("TRACE/sharing/token/resolver: check path: %r", path)
|
||||
if path.startswith("/.token/"):
|
||||
pattern = re.compile('^(/\\.token/' + TOKEN_PATTERN_V1 + '/)$')
|
||||
match = pattern.match(path)
|
||||
if not match:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/token: unsupported token: %r", path)
|
||||
return None
|
||||
logger.debug("TRACE/sharing/token/resolver: unsupported token: %r", path)
|
||||
return {'error': 'token-not-supported'}
|
||||
else:
|
||||
# TODO add token validity checks
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/token: supported token found in path: %r (token=%r)", path, match[1])
|
||||
logger.debug("TRACE/sharing/token/resolver: supported token: %r", path)
|
||||
result = self.database_get_sharing(
|
||||
ShareType="token",
|
||||
OnlyEnabled=False,
|
||||
PathOrToken=match[1])
|
||||
if result is not None:
|
||||
logger.info("Sharing/%s: resolved %r->%r, User=%r, Permissions=%r Conversion=%r", "token", path, result['PathMapped'], result['Owner'], result['Permissions'], result['Conversion'])
|
||||
|
||||
if result is None:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/token/resolver: supported token not found: %r", path)
|
||||
return {'error': 'token-not-found'}
|
||||
|
||||
if result['EnabledByOwner'] is not True:
|
||||
logger.info("Sharing/%s: resolved path %r->%r, User=%r not enabled by owner", "token", path, result['PathMapped'], result['Owner'])
|
||||
return {'error': 'token-not-enabled'}
|
||||
|
||||
logger.info("Sharing/%s: resolved %r->%r, User=%r, Permissions=%r Conversion=%r", "token", path, result['PathMapped'], result['Owner'], result['Permissions'], result['Conversion'])
|
||||
return result
|
||||
else:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/token: no supported prefix found in path: %r", path)
|
||||
logger.debug("TRACE/sharing/token/resolver: no supported prefix found in path: %r", path)
|
||||
return None
|
||||
else:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
@@ -483,13 +493,14 @@ class BaseSharing:
|
||||
if self.sharing_collection_by_map:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/map/resolver: check path: %r", path)
|
||||
|
||||
result = self.database_get_sharing(
|
||||
ShareType="map",
|
||||
PathOrToken=path,
|
||||
User=user)
|
||||
if result:
|
||||
pass
|
||||
else:
|
||||
ShareType="map",
|
||||
PathOrToken=path,
|
||||
OnlyEnabled=False,
|
||||
User=user)
|
||||
|
||||
if not result:
|
||||
# fallback to parent path
|
||||
parent_path = pathutils.parent_path(path)
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
@@ -497,6 +508,7 @@ class BaseSharing:
|
||||
result = self.database_get_sharing(
|
||||
ShareType="map",
|
||||
PathOrToken=parent_path,
|
||||
OnlyEnabled=False,
|
||||
User=user)
|
||||
if result:
|
||||
result['PathMapped'] = path.replace(parent_path, result['PathMapped'])
|
||||
@@ -504,55 +516,26 @@ class BaseSharing:
|
||||
logger.debug("TRACE/sharing/map/resolver: PathMapped=%r Permissions=%r by parent_path=%r", result['PathMapped'], result['Permissions'], parent_path)
|
||||
else:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/map: not found")
|
||||
logger.debug("TRACE/sharing/map/resolver: not found")
|
||||
return None
|
||||
|
||||
logger.info("Sharing/%s: resolved path %r->%r, user %r->%r, Permissions=%r Conversion=%r", "map", path, result['PathMapped'], user, result['Owner'], result['Permissions'], result['Conversion'])
|
||||
return result
|
||||
if result:
|
||||
if result['EnabledByOwner'] is not True:
|
||||
logger.info("Sharing/%s: resolved path %r->%r, user %r->%r not enabled by owner", "map", path, result['PathMapped'], user, result['Owner'])
|
||||
return {'error': 'map-not-enabled'}
|
||||
if result['EnabledByUser'] is not True:
|
||||
logger.info("Sharing/%s: resolved path %r->%r, user %r->%r not enabled by user", "map", path, result['PathMapped'], user, result['Owner'])
|
||||
return {'error': 'map-not-enabled'}
|
||||
|
||||
logger.info("Sharing/%s: resolved path %r->%r, user %r->%r, Permissions=%r Conversion=%r", "map", path, result['PathMapped'], user, result['Owner'], result['Permissions'], result['Conversion'])
|
||||
return result
|
||||
|
||||
return None
|
||||
else:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/map: not active")
|
||||
return None
|
||||
|
||||
# resolves a bday "path" to a share
|
||||
def sharing_collection_by_bday_resolver(self, path: str, user: str) -> Union[dict, None]:
|
||||
""" returning dict with PathMapped, Owner, Permissions or None if invalid"""
|
||||
if self.sharing_collection_by_bday:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/bday/resolver: check path: %r", path)
|
||||
result = self.database_get_sharing(
|
||||
ShareType="bday",
|
||||
PathOrToken=path,
|
||||
User=user)
|
||||
if result:
|
||||
pass
|
||||
else:
|
||||
# fallback to parent path
|
||||
parent_path = pathutils.parent_path(path)
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/bday/resolver: check parent path: %r", parent_path)
|
||||
result = self.database_get_sharing(
|
||||
ShareType="bday",
|
||||
PathOrToken=parent_path,
|
||||
User=user)
|
||||
if result:
|
||||
result['PathMapped'] = path.replace(parent_path, result['PathMapped'])
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/bday/resolver: PathMapped=%r Permissions=%r by parent_path=%r", result['PathMapped'], result['Permissions'], parent_path)
|
||||
else:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/bday: not found")
|
||||
return None
|
||||
|
||||
if not result['Conversion']:
|
||||
result['Conversion'] = "bday"
|
||||
logger.info("Sharing/%s: resolved path %r->%r, user %r->%r, Permissions=%r Conversion=%r", "bday", path, result['PathMapped'], user, result['Owner'], result['Permissions'], result['Conversion'])
|
||||
return result
|
||||
else:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/bday: not active")
|
||||
return None
|
||||
|
||||
# *** POST API ***
|
||||
def post(self, environ: types.WSGIEnviron, base_prefix: str, path: str, user: str) -> types.WSGIResponse:
|
||||
# Late import to avoid circular dependency in config
|
||||
@@ -567,27 +550,28 @@ class BaseSharing:
|
||||
``user`` is empty for anonymous users.
|
||||
|
||||
Request:
|
||||
action: (token|map|bday)/list
|
||||
action: (token|map)/list
|
||||
PathOrToken: <path|token> (optional for filter)
|
||||
|
||||
action: (token|map|bday)/create
|
||||
action: (token|map)/create
|
||||
PathMapped: <path> (mandatory)
|
||||
Permissions: <Permissions> (default: r)
|
||||
|
||||
token -> returns <token>
|
||||
|
||||
map|bday
|
||||
map
|
||||
PathOrToken: <path> (mandatory)
|
||||
User: <target_user> (mandatory)
|
||||
Conversion: None|bday (optional)
|
||||
|
||||
action: (token|map|bday)/update
|
||||
action: (token|map)/update
|
||||
|
||||
action: (token|map|bday)/(delete|disable|enable|hide|unhide)
|
||||
action: (token|map)/(delete|disable|enable|hide|unhide)
|
||||
PathOrToken: <path|token> (mandatory)
|
||||
|
||||
token
|
||||
|
||||
map|bday
|
||||
map
|
||||
PathMapped: <path> (mandatory)
|
||||
User: <target_user>
|
||||
|
||||
@@ -648,11 +632,6 @@ class BaseSharing:
|
||||
logger.warning(api_info + ": not enabled by config (collection_by_map)")
|
||||
return httputils.NOT_FOUND
|
||||
|
||||
if not self.sharing_collection_by_bday and ShareType == "bday":
|
||||
# API "token" is not enabled
|
||||
logger.warning(api_info + ": not enabled by config (collection_by_bday)")
|
||||
return httputils.NOT_FOUND
|
||||
|
||||
# check for valid API hooks
|
||||
if action not in API_HOOKS_V1:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
@@ -830,11 +809,10 @@ class BaseSharing:
|
||||
Properties = request_data['Properties']
|
||||
|
||||
if 'Conversion' in request_data:
|
||||
# verify against whitelist
|
||||
for entry in request_data['Conversion']:
|
||||
if entry not in CONVERSIONS_WHITELIST:
|
||||
return httputils.bad_request("Conversion not supported: %r" % entry)
|
||||
Conversion = request_data['Conversion']
|
||||
# verify against whitelist
|
||||
if Conversion not in CONVERSIONS_WHITELIST:
|
||||
return httputils.bad_request("Conversion not supported: %r" % Conversion)
|
||||
|
||||
if 'Actions' in request_data:
|
||||
return httputils.bad_request("Actions currently not supported (reserved for future needs)")
|
||||
@@ -860,7 +838,12 @@ class BaseSharing:
|
||||
answer['ApiVersion'] = 1
|
||||
Timestamp = int((datetime.now() - datetime(1970, 1, 1)).total_seconds())
|
||||
|
||||
if not self.sharing_collection_by_map and not self.sharing_collection_by_token and not self.sharing_collection_by_bday:
|
||||
if "SHARING_NO_LEGACY" not in os.environ: # TODO: remove/3.7.0-final
|
||||
if ShareType == "bday": # TODO: remove/3.7.0-final
|
||||
ShareType = "map" # TODO: remove/3.7.0-final
|
||||
Conversion = "bday" # TODO: remove/3.7.0-final
|
||||
|
||||
if not self.sharing_collection_by_map and not self.sharing_collection_by_token:
|
||||
if not action == 'info':
|
||||
# API is not enabled
|
||||
logger.warning(api_info + ": API is not enabled")
|
||||
@@ -880,12 +863,16 @@ class BaseSharing:
|
||||
ShareType=ShareType,
|
||||
OwnerOrUser=user,
|
||||
PathMapped=PathMapped,
|
||||
PathOrToken=PathOrToken)
|
||||
PathOrToken=PathOrToken,
|
||||
Conversion=Conversion,
|
||||
)
|
||||
else:
|
||||
result_array = self.database_list_sharing(
|
||||
OwnerOrUser=user,
|
||||
PathMapped=PathMapped,
|
||||
PathOrToken=PathOrToken)
|
||||
PathOrToken=PathOrToken,
|
||||
Conversion=Conversion,
|
||||
)
|
||||
|
||||
answer['Lines'] = len(result_array)
|
||||
if len(result_array) == 0:
|
||||
@@ -905,6 +892,9 @@ class BaseSharing:
|
||||
logger.warning(api_info + ": missing PathMapped")
|
||||
return httputils.bad_request("Missing PathMapped")
|
||||
|
||||
if Conversion is None:
|
||||
Conversion = "none"
|
||||
|
||||
# check whether collection exists
|
||||
with self._storage.acquire_lock("r", user, path=PathMapped):
|
||||
item = next(iter(self._storage.discover(PathMapped)), None)
|
||||
@@ -912,7 +902,12 @@ class BaseSharing:
|
||||
logger.warning(api_info + ": cannot find PathMapped=%r", PathMapped)
|
||||
return httputils.NOT_FOUND
|
||||
if not isinstance(item, storage.BaseCollection):
|
||||
logger.warning(api_info + ": PathMapped=%r is not a collection", PathMapped)
|
||||
return httputils.METHOD_NOT_ALLOWED
|
||||
if Conversion == "bday":
|
||||
if item.tag != "VADDRESSBOOK":
|
||||
logger.warning(api_info + ": PathMapped=%r is not a VADDRESSBOOK collection (mandatory for Conversion=%r)", PathMapped, Conversion)
|
||||
return httputils.METHOD_NOT_ALLOWED
|
||||
|
||||
if Permissions is None:
|
||||
if ShareType == "token":
|
||||
@@ -924,6 +919,12 @@ class BaseSharing:
|
||||
Permissions = "r"
|
||||
else:
|
||||
Permissions = str(Permissions)
|
||||
if Conversion == "bday":
|
||||
# bday is read-only
|
||||
for permission in Permissions:
|
||||
if permission not in "r":
|
||||
logger.warning(api_info + ": PathMapped=%r Permissions=%r not supported for Conversion=%r", PathMapped, Permissions, Conversion)
|
||||
return httputils.METHOD_NOT_ALLOWED
|
||||
|
||||
if Enabled is None:
|
||||
Enabled = False # security by default
|
||||
@@ -988,7 +989,7 @@ class BaseSharing:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/" + api_info + ": result=%r", result)
|
||||
|
||||
elif ShareType in ["map", "bday"]:
|
||||
elif ShareType == "map":
|
||||
# check preconditions
|
||||
if PathOrToken is None:
|
||||
return httputils.bad_request("Missing PathOrToken")
|
||||
@@ -1006,10 +1007,10 @@ class BaseSharing:
|
||||
else:
|
||||
User = str(User)
|
||||
|
||||
# lookup existing shares with requested PathMapped for same User
|
||||
shares = self.database_list_sharing(ShareType=ShareType, PathMapped=PathMapped, User=User)
|
||||
# lookup existing shares with requested PathMapped for same User and same Conversion
|
||||
shares = self.database_list_sharing(ShareType=ShareType, PathMapped=PathMapped, User=User, Conversion=Conversion)
|
||||
if len(shares) > 0:
|
||||
logger.warning(api_info + ": share already exists with PathMapped=%r User=%r", PathMapped, User)
|
||||
logger.warning(api_info + ": share already exists with PathMapped=%r User=%r Conversion=%r", PathMapped, User, Conversion)
|
||||
return httputils.CONFLICT
|
||||
|
||||
# check access Permissions
|
||||
@@ -1018,26 +1019,14 @@ class BaseSharing:
|
||||
logger.warning(api_info + ": access to PathMapped=%r not allowed for owner %r", PathMapped, user)
|
||||
return httputils.NOT_ALLOWED
|
||||
|
||||
if ShareType == "map":
|
||||
if self.permit_create_map is False:
|
||||
if "m" not in access.permissions:
|
||||
logger.warning(api_info + ": access to PathMapped=%r not allowed for owner %r (permit=False but explicit grant misses 'm')", PathMapped, user)
|
||||
return httputils.NOT_ALLOWED
|
||||
else:
|
||||
if "M" in access.permissions:
|
||||
logger.warning(api_info + ": access to PathMapped=%r not allowed for owner %r (permit=True but denied by 'M')", PathMapped, user)
|
||||
return httputils.NOT_ALLOWED
|
||||
|
||||
elif ShareType == "bday":
|
||||
Conversion = "bday"
|
||||
if self.permit_create_bday is False:
|
||||
if "b" not in access.permissions:
|
||||
logger.warning(api_info + ": access to PathMapped=%r not allowed for owner %r (permit=False but explicit grant misses 'b')", PathMapped, user)
|
||||
return httputils.NOT_ALLOWED
|
||||
else:
|
||||
if "B" in access.permissions:
|
||||
logger.warning(api_info + ": access to PathMapped=%r not allowed for owner %r (permit=True but denied by 'B')", PathMapped, user)
|
||||
return httputils.NOT_ALLOWED
|
||||
if self.permit_create_map is False:
|
||||
if "m" not in access.permissions:
|
||||
logger.warning(api_info + ": access to PathMapped=%r not allowed for owner %r (permit=False but explicit grant misses 'm')", PathMapped, user)
|
||||
return httputils.NOT_ALLOWED
|
||||
else:
|
||||
if "M" in access.permissions:
|
||||
logger.warning(api_info + ": access to PathMapped=%r not allowed for owner %r (permit=True but denied by 'M')", PathMapped, user)
|
||||
return httputils.NOT_ALLOWED
|
||||
|
||||
access = Access(self._rights, User, PathOrToken)
|
||||
if not access.check("r"):
|
||||
@@ -1100,7 +1089,7 @@ class BaseSharing:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/" + api_info + ": start")
|
||||
|
||||
if ShareType not in ["token", "map", "bday"]:
|
||||
if ShareType not in SHARE_TYPES_V1:
|
||||
logger.warning(api_info + ": unsupported for ShareType=%r", ShareType)
|
||||
return httputils.bad_request("Invalid share type")
|
||||
|
||||
@@ -1226,7 +1215,7 @@ class BaseSharing:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/" + api_info + ": start")
|
||||
|
||||
if ShareType not in ["token", "map", "bday"]:
|
||||
if ShareType not in SHARE_TYPES_V1:
|
||||
logger.warning(api_info + ": unsupported for ShareType=%r", ShareType)
|
||||
return httputils.bad_request("Invalid share type")
|
||||
|
||||
@@ -1268,19 +1257,21 @@ class BaseSharing:
|
||||
if ShareType in ["all", "map"]:
|
||||
answer['FeatureEnabledCollectionByMap'] = self.sharing_collection_by_map
|
||||
answer['PermittedCreateCollectionByMap'] = self.permit_create_map
|
||||
if "SHARING_NO_LEGACY" not in os.environ: # TODO: remove/3.7.0-final
|
||||
answer['FeatureEnabledCollectionByBday'] = self.sharing_collection_by_map # TODO: remove/3.7.0-final
|
||||
answer['PermittedCreateCollectionByBday'] = self.permit_create_map # TODO: remove/3.7.0-final
|
||||
if ShareType in ["all", "token"]:
|
||||
answer['FeatureEnabledCollectionByToken'] = self.sharing_collection_by_token
|
||||
answer['PermittedCreateCollectionByToken'] = self.permit_create_token
|
||||
if ShareType in ["all", "bday"]:
|
||||
answer['FeatureEnabledCollectionByBday'] = self.sharing_collection_by_bday
|
||||
answer['PermittedCreateCollectionByBday'] = self.permit_create_bday
|
||||
if ShareType in ["all", "map", "token"]:
|
||||
answer['SupportedConversions'] = CONVERSIONS_WHITELIST
|
||||
|
||||
# action: TOGGLE
|
||||
elif action in API_SHARE_TOGGLES_V1:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/sharing/API/POST/" + action)
|
||||
|
||||
if ShareType not in ["token", "map", "bday"]:
|
||||
if ShareType not in SHARE_TYPES_V1:
|
||||
logger.warning(api_info + ": unsupported for ShareType=%r", ShareType)
|
||||
return httputils.bad_request("Invalid share type")
|
||||
|
||||
@@ -1365,6 +1356,8 @@ class BaseSharing:
|
||||
if key != 'Content':
|
||||
if API_TYPES_V1[key] is bool or API_TYPES_V1[key] is int:
|
||||
answer_array.append(key + '=' + str(answer[key]))
|
||||
elif API_TYPES_V1[key] is list:
|
||||
answer_array.append(key + '=(' + str(" ".join(answer[key])) + ')')
|
||||
else:
|
||||
answer_array.append(key + "='" + str(answer[key]) + "'")
|
||||
if 'Content' in answer and answer['Content'] is not None:
|
||||
|
||||
Reference in New Issue
Block a user