Files
Radicale/radicale/tests/test_sharing.py
Peter Bieringer acf5741a89 sharing: cosmetics
2026-03-22 08:22:10 +01:00

4696 lines
237 KiB
Python

# This file is part of Radicale - CalDAV and CardDAV server
# Copyright © 2026-2026 Peter Bieringer <pb@bieringer.de>
# Copyright © 2026-2026 Max Berger <max@berger.name>
#
# This library is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Radicale. If not, see <http://www.gnu.org/licenses/>.
"""
Radicale tests related to sharing.
"""
import json
import logging
import os
import re
from typing import Dict, Sequence, Tuple, Union
from radicale import sharing, xmlutils
from radicale.tests import BaseTest
from radicale.tests.helpers import get_file_content
class TestSharingApiSanity(BaseTest):
"""Tests with sharing."""
htpasswd_file_path: str
# Setup
def setup_method(self) -> None:
BaseTest.setup_method(self)
self.htpasswd_file_path = os.path.join(self.colpath, ".htpasswd")
encoding: str = self.configuration.get("encoding", "stock")
htpasswd = ["owner:ownerpw", "user:userpw",
"owner1:owner1pw", "user1:user1pw",
"owner2:owner2pw", "user2:user2pw"]
htpasswd_content = "\n".join(htpasswd)
with open(self.htpasswd_file_path, "w", encoding=encoding) as f:
f.write(htpasswd_content)
# Helper functions
def _sharing_api(self, sharing_type: str, action: str, check: int, login: Union[str, None], data: str, content_type: str, accept: Union[str, None]) -> Tuple[int, Dict[str, str], str]:
path_base = "/.sharing/v1/" + sharing_type + "/"
_, headers, answer = self.request("POST", path_base + action, check=check, login=login, data=data, content_type=content_type, accept=accept)
logging.info("received answer:\n%s", "\n".join(answer.splitlines()))
return _, headers, answer
def _sharing_api_form(self, sharing_type: str, action: str, check: int, login: Union[str, None], form_array: Sequence[str], accept: Union[str, None] = None) -> Tuple[int, Dict[str, str], str]:
data = "&".join(form_array)
content_type = "application/x-www-form-urlencoded"
if accept is None:
accept = "text/plain"
_, headers, answer = self._sharing_api(sharing_type, action, check, login, data, content_type, accept)
return _, headers, answer
def _sharing_api_json(self, sharing_type: str, action: str, check: int, login: Union[str, None], json_dict: dict, accept: Union[str, None] = None) -> Tuple[int, Dict[str, str], str]:
data = json.dumps(json_dict)
content_type = "application/json"
if accept is None:
accept = "application/json"
_, headers, answer = self._sharing_api(sharing_type, action, check, login, data, content_type, accept)
return _, headers, answer
def _propfind_allprop(self, path: str, login) -> dict:
propfind_allprop = get_file_content("allprop.xml")
_, responses = self.propfind(path=path, data=propfind_allprop, login=login)
logging.info("response: %r", responses)
response = responses[path]
assert not isinstance(response, int)
return response
def _propfind_priviledges(self, path: str, login) -> list[str]:
response = self._propfind_allprop(path, login)
status, prop = response["D:current-user-privilege-set"]
logging.debug("prop: %r", prop)
priviledges = prop.findall(xmlutils.make_clark("D:privilege"))
assert len(priviledges) >= 1
priviledges_list = [xmlutils.make_human_tag(priviledge.findall("*")[0].tag) for priviledge in priviledges]
return priviledges_list
def _propfind_calendar_color(self, path, login) -> Union[str, None]:
propfind_calendar_color = get_file_content("propfind_calendar_color.xml")
_, responses = self.propfind(path=path, data=propfind_calendar_color, login=login)
logging.info("response: %r", responses)
response = responses[path]
assert not isinstance(response, int)
status, prop = response["ICAL:calendar-color"]
logging.debug("ICAL:calendar-color: %r", prop.text)
assert status == 200
return prop.text
def _proppatch_calendar_color(self, path, login, color) -> None:
_, responses = self.proppatch(path=path, data="""\
<?xml version="1.0" encoding="utf-8"?>
<D:propertyupdate xmlns:D="DAV:">
<D:set>
<D:prop>
<I:calendar-color xmlns:I="http://apple.com/ns/ical/">""" + color + """</I:calendar-color>
</D:prop>
</D:set>
</D:propertyupdate>""", login=login)
logging.info("response: %r", responses)
response = responses[path]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["ICAL:calendar-color"]
assert status == 200 and not prop.text
return
def _proppatch_calendar_color_remove(self, path, login) -> None:
_, responses = self.proppatch(path=path, data="""\
<?xml version="1.0" encoding="utf-8"?>
<D:propertyupdate xmlns:D="DAV:">
<D:remove>
<D:prop>
<I:calendar-color xmlns:I="http://apple.com/ns/ical/" />
</D:prop>
</D:remove>
</D:propertyupdate>""", login=login)
logging.info("response: %r", responses)
response = responses[path]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["ICAL:calendar-color"]
assert status == 200 and not prop.text
return
def _propfind_calendar_description(self, path, login):
_, responses = self.propfind(path=path, data="""\
<?xml version="1.0" encoding="utf-8"?>
<D:propfind xmlns:D="DAV:">
<D:prop>
<C:calendar-description xmlns:C="urn:ietf:params:xml:ns:caldav" />
</D:prop>
</D:propfind>""", login=login)
logging.info("response: %r", responses)
response = responses[path]
assert not isinstance(response, int)
status, prop = response["C:calendar-description"]
logging.debug("C:calendar-description: %r", prop.text)
assert status == 200
return prop.text
def _proppatch_calendar_description(self, path, login, description) -> None:
_, responses = self.proppatch(path=path, data="""\
<?xml version="1.0" encoding="utf-8"?>
<D:propertyupdate xmlns:D="DAV:">
<D:set>
<D:prop>
<C:calendar-description xmlns:C="urn:ietf:params:xml:ns:caldav">""" + description + """</C:calendar-description>
</D:prop>
</D:set>
</D:propertyupdate>""", login=login)
logging.info("response: %r", responses)
response = responses[path]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["C:calendar-description"]
assert status == 200 and not prop.text
return
def _proppatch_calendar_description_remove(self, path, login) -> None:
_, responses = self.proppatch(path=path, data="""\
<?xml version="1.0" encoding="utf-8"?>
<D:propertyupdate xmlns:D="DAV:">
<D:remove>
<D:prop>
<C:calendar-description xmlns:C="urn:ietf:params:xml:ns:caldav" />
</D:prop>
</D:remove>
</D:propertyupdate>""", login=login)
logging.info("response: %r", responses)
response = responses[path]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["C:calendar-description"]
assert status == 200 and not prop.text
return
# Test functions
def test_sharing_api_base_csv_custom(self) -> None:
self.database_path = os.path.join(self.colpath, "collection-db/test.csv")
self.configure({"sharing": {
"type": "csv",
"database_path": self.database_path,
"collection_by_map": "True",
"collection_by_token": "False"}
})
def test_sharing_api_base_no_auth(self) -> None:
"""POST request at '/.sharing' without authentication."""
# disabled
for path in ["/.sharing", "/.sharing/"]:
_, headers, _ = self.request("POST", path, check=404)
path = "/.sharing/"
# no database is active
logging.info("\n*** check API hook base: map=True token=False")
self.configure({"sharing": {
"collection_by_map": "True",
"collection_by_token": "False"}
})
_, headers, _ = self.request("POST", path, check=404)
logging.info("\n*** check API hook base: map=False token=True")
self.configure({"sharing": {
"collection_by_map": "False",
"collection_by_token": "True"}
})
_, headers, _ = self.request("POST", path, check=404)
logging.info("\n*** check API hook base: map=True token=True")
self.configure({"sharing": {
"collection_by_map": "True",
"collection_by_token": "True"}
})
_, headers, _ = self.request("POST", path, check=404)
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# no database is active
logging.info("\n*** check API hook base: map=True token=False")
self.configure({"sharing": {
"collection_by_map": "True",
"collection_by_token": "False"}
})
_, headers, _ = self.request("POST", path, check=401)
logging.info("\n*** check API hook base: map=False token=True")
self.configure({"sharing": {
"collection_by_map": "False",
"collection_by_token": "True"}
})
_, headers, _ = self.request("POST", path, check=401)
logging.info("\n*** check API hook base: map=True token=True")
self.configure({"sharing": {
"collection_by_map": "True",
"collection_by_token": "True"}
})
_, headers, _ = self.request("POST", path, check=401)
def test_sharing_api_base_with_auth(self) -> None:
"""POST request at '/.sharing' with authentication."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"collection_by_map": "True",
"collection_by_bday": "True",
"collection_by_token": "True"},
"rights": {"type": "owner_only"}})
form_array: Sequence[str]
json_dict: dict
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
for path in ["/.sharing/", "/.sharing/v9/"]:
logging.info("\n*** check invalid API URI: %r", path)
_, headers, _ = self.request("POST", path, check=404, login="owner:ownerpw")
# path with valid API but no hook
for path in ["/.sharing/v1/"]:
logging.info("\n*** check valid API URI without hook: %r", path)
_, headers, _ = self.request("POST", path, check=404, login="owner:ownerpw")
# path with valid API and hook but not enabled "map"
self.configure({"sharing": {
"collection_by_map": "False",
"collection_by_token": "True"}
})
sharetype = "map"
for action in sharing.API_HOOKS_V1:
path = "/.sharing/v1/" + sharetype + "/" + action
logging.info("\n*** check valid API URI hook (but not enabled): %r", path)
_, headers, _ = self.request("POST", path, check=404, login="owner:ownerpw")
# path with valid API and hook but not enabled "token"
self.configure({"sharing": {
"collection_by_map": "True",
"collection_by_token": "False",
"permit_create_map": "False",
"permit_create_token": "False"}
})
sharetype = "token"
for action in sharing.API_HOOKS_V1:
path = "/.sharing/v1/" + sharetype + "/" + action
logging.info("\n*** check valid API URI hook (but not enabled): %r", path)
_, headers, _ = self.request("POST", path, check=404, login="owner:ownerpw")
# check info hook
logging.info("\n*** check API hook: info/all (text)")
form_array = []
_, headers, answer = self._sharing_api_form("all", "info", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "PermittedCreateCollectionByMap=False" in answer
assert "PermittedCreateCollectionByToken=False" in answer
assert "FeatureEnabledCollectionByMap=True" in answer
assert "FeatureEnabledCollectionByToken=False" in answer
logging.info("\n*** check API hook: info/all (json)")
json_dict = {}
_, headers, answer = self._sharing_api_json("all", "info", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['FeatureEnabledCollectionByMap'] is True, f'FeatureEnabledCollectionByMap {db_type}'
assert answer_dict['FeatureEnabledCollectionByToken'] is False, f'FeatureEnabledCollectionByToken {db_type}'
assert answer_dict['PermittedCreateCollectionByMap'] is False, f'PermittedCreateCollectionByMap {db_type}'
assert answer_dict['PermittedCreateCollectionByToken'] is False, f'PermittedCreateCollectionByToken {db_type}'
logging.info("\n*** check API hook: info/map")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "info", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['FeatureEnabledCollectionByMap'] is True
assert 'FeatureEnabledCollectionByToken' not in answer_dict
assert 'PermittedCreateCollectionByToken' not in answer_dict
logging.info("\n*** check API hook: info/token -> 404 (not enabled)")
json_dict = {}
_, headers, answer = self._sharing_api_json("token", "info", check=404, login="owner:ownerpw", json_dict=json_dict)
# path with valid API and hook and all enabled
self.configure({"sharing": {
"collection_by_map": "True",
"collection_by_bday": "True",
"collection_by_token": "True"}
})
for sharetype in sharing.SHARE_TYPES:
path = "/.sharing/v1/" + sharetype + "/" + action
# invalid API
logging.info("\n*** check hook -> 404 (invalid)")
_, headers, _ = self.request("POST", path + "NA", check=404, login="owner:ownerpw")
# valid API
logging.info("\n*** check hook -> 400 (valid but no data)")
_, headers, _ = self.request("POST", path, check=400, login="owner:ownerpw")
logging.info("\n*** check API hook: info/token -> 200")
json_dict = {}
_, headers, answer = self._sharing_api_json("token", "info", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['FeatureEnabledCollectionByToken'] is True
assert 'FeatureEnabledCollectionByMap' not in answer_dict
assert 'PermittedCreateCollectionByMap' not in answer_dict
# When turning on permission to create
self.configure({"sharing": {
"collection_by_map": "True",
"collection_by_bday": "True",
"collection_by_token": "True",
"permit_create_map": "True",
"permit_create_bday": "True",
"permit_create_token": "True"}
})
logging.info("\n*** check API hook: info/all")
json_dict = {}
_, headers, answer = self._sharing_api_json("all", "info", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['FeatureEnabledCollectionByMap'] is True, f'FeatureEnabledCollectionByMap {db_type}'
assert answer_dict['FeatureEnabledCollectionByToken'] is True, f'FeatureEnabledCollectionByToken {db_type}'
assert answer_dict['FeatureEnabledCollectionByBday'] is True, f'FeatureEnabledCollectionByBday {db_type}'
assert answer_dict['PermittedCreateCollectionByMap'] is True, f'PermittedCreateCollectionByMap {db_type}'
assert answer_dict['PermittedCreateCollectionByToken'] is True, f'PermittedCreateCollectionByToken {db_type}'
assert answer_dict['PermittedCreateCollectionByBday'] is True, f'PermittedCreateCollectionByBday {db_type}'
def test_sharing_api_list_with_auth(self) -> None:
"""POST/list with authentication."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_bday": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_bday": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "true",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
form_array: Sequence[str]
json_dict: dict
self.mkcalendar("/owner/collectionL1/", login="owner:ownerpw")
self.mkcalendar("/owner/collectionL2/", login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
action = "list"
for sharing_type in sharing.SHARE_TYPES:
logging.info("\n*** list (without form) -> should fail")
path = "/.sharing/v1/" + sharing_type + "/" + action
_, headers, _ = self.request("POST", path, check=400, login="owner:ownerpw")
logging.info("\n*** list (form->csv)")
form_array = []
_, headers, answer = self._sharing_api_form(sharing_type, "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='not-found'" in answer
assert "Lines=0" in answer
logging.info("\n*** list (json->text)")
json_dict = {}
_, headers, answer = self._sharing_api_json(sharing_type, "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/plain")
assert "Status='not-found'" in answer
assert "Lines=0" in answer
logging.info("\n*** list (json->json)")
json_dict = {}
_, headers, answer = self._sharing_api_json(sharing_type, "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "not-found"
assert answer_dict['Lines'] == 0
logging.info("\n*** create a token -> 200")
form_array = ["PathMapped=/owner/collectionL1/"]
_, headers, answer = self._sharing_api_form("token", "create", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "PathOrToken='" in answer
# extract token
match = re.search("PathOrToken='(.+)'", answer)
if match:
token = match.group(1)
logging.info("received token %r", token)
else:
assert False
logging.info("\n*** create a map -> 200")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = "/owner/collectionL2/"
json_dict['PathOrToken'] = "/user/collectionL2-shared-by-owner/"
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** list/all (form->csv)")
form_array = []
_, headers, answer = self._sharing_api_form("all", "list", check=200, login="owner:ownerpw", form_array=form_array, accept="text/csv")
assert "Status=" not in answer
assert "Line=" not in answer
assert "ShareType" in answer
assert "token" in answer
assert "map" in answer
logging.info("\n*** list/all (form->text)")
form_array = []
_, headers, answer = self._sharing_api_form("all", "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "Lines=2" in answer
assert "Fields=" in answer
assert "Content[0]=" in answer
assert "Content[1]=" in answer
logging.info("\n*** delete token -> 200")
form_array = ["PathOrToken=" + token]
_, headers, answer = self._sharing_api_form("token", "delete", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
logging.info("\n*** delete share -> 200")
form_array = []
form_array.append("PathOrToken=/user/collectionL2-shared-by-owner/")
form_array.append("PathMapped=/owner/collectionL2/")
_, headers, answer = self._sharing_api_form("map", "delete", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
def test_sharing_api_token_basic(self) -> None:
"""share-by-token API tests."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_bday": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
form_array: Sequence[str]
json_dict: dict
path_base1 = "/owner/collection1.ics/"
path_base2 = "/owner/collection2.ics/"
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
logging.info("\n*** create token without PathMapped (form) -> 400")
form_array = []
_, headers, answer = self._sharing_api_form("token", "create", 400, login="owner:ownerpw", form_array=form_array)
logging.info("\n*** create token without PathMapped (json) -> 400")
json_dict = {}
_, headers, answer = self._sharing_api_json("token", "create", 400, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create token#1 without existing collection (form->text) -> 404")
form_array = ["PathMapped=" + path_base1]
_, headers, answer = self._sharing_api_form("token", "create", check=404, login="owner:ownerpw", form_array=form_array)
logging.info("\n*** create collection*")
self.mkcalendar(path_base1, login="owner:ownerpw")
self.mkcalendar(path_base2, login="owner:ownerpw")
logging.info("\n*** create token#1 with existing collection (form->text) but no trailing / -> 400")
form_array = ["PathMapped=" + path_base1.rstrip('/')]
_, headers, answer = self._sharing_api_form("token", "create", check=400, login="owner:ownerpw", form_array=form_array)
logging.info("\n*** create token#1 with existing collection (form->text) -> 200")
form_array = ["PathMapped=" + path_base1]
_, headers, answer = self._sharing_api_form("token", "create", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "PathOrToken='" in answer
# extract token
match = re.search("PathOrToken='(.+)'", answer)
if match:
token1 = match.group(1)
logging.info("received token %r", token1)
else:
assert False
logging.info("\n*** create token#2 (json->text)")
json_dict = {'PathMapped': path_base2}
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/plain")
assert "Status='success'" in answer
assert "Token=" in answer
# extract token
match = re.search("Token='(.+)'", answer)
if match:
token2 = match.group(1)
logging.info("received token %r", token2)
else:
assert False
logging.info("\n*** lookup token#1 (form->text)")
form_array = ["PathOrToken=" + token1]
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "Lines=1" in answer
assert path_base1 in answer
logging.info("\n*** lookup token#2 (json->text")
json_dict = {'PathOrToken': token2}
_, headers, answer = self._sharing_api_json("token", "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/plain")
assert "Status='success'" in answer
assert "Lines=1" in answer
assert path_base2 in answer
logging.info("\n*** lookup token#2 (json->json)")
json_dict = {'PathOrToken': token2}
_, headers, answer = self._sharing_api_json("token", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['PathMapped'] == path_base2
logging.info("\n*** lookup tokens (form->text)")
form_array = []
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "Lines=2" in answer
assert path_base1 in answer
assert path_base2 in answer
logging.info("\n*** lookup tokens (form->csv)")
form_array = []
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array, accept="text/csv")
assert "Status='success'" not in answer
assert "Lines=2" not in answer
assert ";".join(sharing.DB_FIELDS_V1) in answer
assert path_base1 in answer
assert path_base2 in answer
logging.info("\n*** delete token#1 (form->text)")
form_array = ["PathOrToken=" + token1]
_, headers, answer = self._sharing_api_form("token", "delete", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
logging.info("\n*** lookup token#1 (form->text) -> should not be there anymore")
form_array = ["PathOrToken=" + token1]
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='not-found'" in answer
assert "Lines=0" in answer
logging.info("\n*** lookup tokens (form->text) -> still one should be there")
form_array = []
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "Lines=1" in answer
logging.info("\n*** disable token#2 as owner (form->text)")
form_array = ["PathOrToken=" + token2]
_, headers, answer = self._sharing_api_form("token", "disable", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
logging.info("\n*** lookup token#2 (json->json) -> check for not enabled")
json_dict = {'PathOrToken': token2}
_, headers, answer = self._sharing_api_json("token", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['EnabledByOwner'] is False
logging.info("\n*** enable token#2 as owner (json->json)")
json_dict = {}
json_dict['PathOrToken'] = token2
_, headers, answer = self._sharing_api_json("token", "enable", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** lookup token#2 (form->text) -> check for enabled")
form_array = []
form_array.append("PathOrToken=" + token2)
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "Lines=1" in answer
assert "True;True;True;True" in answer
logging.info("\n*** hide token#2 (form->text)")
form_array = []
form_array.append("PathOrToken=" + token2)
_, headers, answer = self._sharing_api_form("token", "hide", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
logging.info("\n*** lookup token#2 (form->text) -> check for hidden")
form_array = []
form_array.append("PathOrToken=" + token2)
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "Lines=1" in answer
assert "True;True;True;True" in answer
logging.info("\n*** unhide token#2 (json->json)")
json_dict = {}
json_dict['PathOrToken'] = token2
_, headers, answer = self._sharing_api_json("token", "unhide", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** lookup token#2 (json->json) -> check for not hidden")
json_dict = {}
json_dict['PathOrToken'] = token2
_, headers, answer = self._sharing_api_json("token", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['HiddenByOwner'] is False
logging.info("\n*** delete token#2 (json->json)")
json_dict = {}
json_dict['PathOrToken'] = token2
_, headers, answer = self._sharing_api_json("token", "delete", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** lookup token#2 (json->json) -> should not be there anymore")
json_dict = {}
json_dict['PathOrToken'] = token2
_, headers, answer = self._sharing_api_json("token", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "not-found"
assert answer_dict['Lines'] == 0
logging.info("\n*** delete collection*")
self.delete(path_base1, login="owner:ownerpw")
self.delete(path_base2, login="owner:ownerpw")
def test_sharing_api_token_usage(self) -> None:
"""share-by-token API tests - real usage."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
form_array: Sequence[str]
json_dict: dict
path_token = "/.token/"
path_base = "/owner/calendar.ics/"
path_base2 = "/owner/calendar2.ics/"
logging.info("\n*** prepare")
self.mkcalendar(path_base, login="owner:ownerpw")
event = get_file_content("event1.ics")
path = path_base + "/event1.ics"
self.put(path, event, login="owner:ownerpw")
self.mkcalendar(path_base2, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
logging.info("\n*** test access to collection")
_, headers, answer = self.request("GET", path_base, check=200, login="owner:ownerpw")
assert "UID:event" in answer
logging.info("\n*** test access to item")
_, headers, answer = self.request("GET", path, check=200, login="owner:ownerpw")
assert "UID:event" in answer
logging.info("\n*** create token")
form_array = []
form_array.append("PathMapped=" + path_base)
_, headers, answer = self._sharing_api_form("token", "create", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "PathOrToken=" in answer
# extract token
match = re.search("PathOrToken='(.+)'", answer)
if match:
token = match.group(1)
logging.info("received token %r", token)
else:
assert False
logging.info("\n*** create token#2")
form_array = []
form_array.append("PathMapped=" + path_base2)
_, headers, answer = self._sharing_api_form("token", "create", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "PathOrToken=" in answer
# extract token
match = re.search("PathOrToken='(.+)'", answer)
if match:
token2 = match.group(1)
logging.info("received token %r", token2)
else:
assert False
logging.info("\n*** enable token (form->text)")
form_array = ["PathOrToken=" + token]
_, headers, answer = self._sharing_api_form("token", "enable", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
logging.info("\n*** fetch collection using invalid token (without credentials)")
_, headers, answer = self.request("GET", path_token + "v1/invalidtoken", check=401)
logging.info("\n*** fetch collection using token (without credentials)")
_, headers, answer = self.request("GET", path_token + token, check=200)
assert "UID:event" in answer
logging.info("\n*** disable token (form->text)")
form_array = ["PathOrToken=" + token]
_, headers, answer = self._sharing_api_form("token", "disable", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
logging.info("\n*** fetch collection using disabled token (without credentials)")
_, headers, answer = self.request("GET", path_token + token, check=401)
logging.info("\n*** enable token (form->text)")
form_array = ["PathOrToken=" + token]
_, headers, answer = self._sharing_api_form("token", "enable", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
logging.info("\n*** fetch collection using token (without credentials)")
_, headers, answer = self.request("GET", path_token + token, check=200)
assert "UID:event" in answer
logging.info("\n*** delete token#2 (json->json)")
json_dict = {}
json_dict['PathOrToken'] = token2
_, headers, answer = self._sharing_api_json("token", "delete", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['ApiVersion'] == 1
assert answer_dict['Status'] == "success"
logging.info("\n*** delete token (json->json)")
json_dict = {'PathOrToken': token}
_, headers, answer = self._sharing_api_json("token", "delete", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['ApiVersion'] == 1
assert answer_dict['Status'] == "success"
logging.info("\n*** delete token (form->text) -> no longer available")
form_array = ["PathOrToken=" + token]
_, headers, answer = self._sharing_api_form("token", "delete", check=404, login="owner:ownerpw", form_array=form_array)
logging.info("\n*** fetch collection using deleted token (without credentials)")
_, headers, answer = self.request("GET", path_token + token, check=401)
def test_sharing_api_map_basic(self) -> None:
"""share-by-map API basic tests."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_owner = "/owner/calendar.ics/"
path_user = "/user/calendar-owner.ics/"
path_user2 = "/user/calendar-owner2.ics/"
self.mkcalendar(path_owner, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
self.configure({"sharing": {"permit_create_map": "False"}})
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
logging.info("\n*** create map without PathMapped (json) -> 400")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "create", 400, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create map without PathMapped but User (json) -> 400")
json_dict = {'User': "user"}
_, headers, answer = self._sharing_api_json("map", "create", 400, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create map without PathMapped but User and PathOrToken (json) -> 400")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_user
_, headers, answer = self._sharing_api_json("map", "create", 400, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create map with PathMapped, User, PathOrToken without trailing / (json) -> 400")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_user
json_dict['PathMapped'] = path_owner.rstrip('/')
_, headers, answer = self._sharing_api_json("map", "create", 400, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create map with PathMapped without trailing /, User, PathOrToken (json) -> 400")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_user.rstrip('/')
json_dict['PathMapped'] = path_owner
_, headers, answer = self._sharing_api_json("map", "create", 400, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create map with PathMapped, User, PathOrToken - not permitted (json) -> 403")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_user
json_dict['PathMapped'] = path_owner
_, headers, answer = self._sharing_api_json("map", "create", 403, login="owner:ownerpw", json_dict=json_dict)
self.configure({"sharing": {"permit_create_map": "True"}})
logging.info("\n*** create map with PathMapped, User, PathOrToken (json) -> 200")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_user
json_dict['PathMapped'] = path_owner
_, headers, answer = self._sharing_api_json("map", "create", 200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create map with PathMapped, User, PathOrToken2 (json) -> 409")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_user2
json_dict['PathMapped'] = path_owner
_, headers, answer = self._sharing_api_json("map", "create", 409, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create map with PathMapped, User, PathOrToken=PathOwner (json) -> 409")
json_dict = {}
json_dict['User'] = "owner"
json_dict['PathOrToken'] = path_owner
json_dict['PathMapped'] = path_owner
_, headers, answer = self._sharing_api_json("map", "create", 409, login="owner:ownerpw", json_dict=json_dict)
def test_sharing_api_map_usage(self) -> None:
"""share-by-map API usage tests."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"permit_properties_overlay": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_header_on_debug": "True",
"response_content_on_debug": "True",
"request_content_on_debug": "False"},
"rights": {"type": "owner_only"}})
json_dict: dict
file_item1 = "event1.ics"
file_item2 = "event2.ics"
path_shared = "/user/calendarU-shared-by-owner.ics/"
path_shared_item1 = os.path.join(path_shared, file_item1)
path_shared_item2 = os.path.join(path_shared, file_item2)
path_mapped = "/owner/calendarU.ics/"
path_mapped_item1 = os.path.join(path_mapped, file_item1)
path_mapped_item2 = os.path.join(path_mapped, file_item2)
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
event = get_file_content(file_item1)
self.put(path_mapped_item1, event, check=201, login="owner:ownerpw")
event = get_file_content(file_item2)
self.put(path_mapped_item2, event, check=201, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
logging.info("\n*** test access to collection")
_, headers, answer = self.request("GET", path_mapped, check=200, login="owner:ownerpw")
assert "UID:event1" in answer
assert "UID:event2" in answer
logging.info("\n*** test access to item")
_, headers, answer = self.request("GET", path_mapped_item1, check=200, login="owner:ownerpw")
assert "UID:event1" in answer
logging.info("\n*** create map with PathMapped and User and PathOrToken (json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
json_dict['Properties'] = {"D:displayname": "Test"}
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** lookup map without filter (json->json)")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['PathOrToken'] == path_shared
assert answer_dict['Content'][0]['PathMapped'] == path_mapped
assert answer_dict['Content'][0]['ShareType'] == "map"
assert answer_dict['Content'][0]['Owner'] == "owner"
assert answer_dict['Content'][0]['User'] == "user"
assert answer_dict['Content'][0]['EnabledByOwner'] is False
assert answer_dict['Content'][0]['EnabledByUser'] is False
assert answer_dict['Content'][0]['HiddenByOwner'] is True
assert answer_dict['Content'][0]['HiddenByUser'] is True
assert answer_dict['Content'][0]['Permissions'] == "r"
assert answer_dict['Content'][0]['Properties'] == {"D:displayname": "Test"}
logging.info("\n*** enable map by owner (json->json) -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** enable map by user (json->json)")
json_dict = {}
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** fetch collection (without credentials)")
_, headers, answer = self.request("GET", path_mapped, check=401)
logging.info("\n*** fetch collection (with credentials) as owner")
_, headers, answer = self.request("GET", path_mapped, check=200, login="owner:ownerpw")
assert "UID:event" in answer
assert 'Content-Disposition' in headers
# fallback title
assert 'Calendar.ics' in headers['Content-Disposition']
logging.info("\n*** fetch item (with credentials) as owner")
_, headers, answer = self.request("GET", path_mapped_item1, check=200, login="owner:ownerpw")
assert "UID:event" in answer
logging.info("\n*** fetch collection (with credentials) as user")
_, headers, answer = self.request("GET", path_mapped, check=403, login="user:userpw")
logging.info("\n*** fetch collection via map (with credentials) as user")
_, headers, answer = self.request("GET", path_shared, check=200, login="user:userpw")
assert "UID:event1" in answer
assert "UID:event2" in answer
assert 'Content-Disposition' in headers
# title from Properties
assert 'Test.ics' in headers['Content-Disposition']
logging.info("\n*** fetch item via map (with credentials) as user")
_, headers, answer = self.request("GET", path_shared_item1, check=200, login="user:userpw")
# only requested event has to be in the answer
assert "UID:event1" in answer
assert "UID:event2" not in answer
logging.info("\n*** fetch item via map (with credentials) as user")
_, headers, answer = self.request("GET", path_shared_item2, check=200, login="user:userpw")
# only requested event has to be in the answer
assert "UID:event2" in answer
assert "UID:event1" not in answer
logging.info("\n*** disable map by owner (json->json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "disable", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** fetch collection via map (with credentials) as user -> n/a")
_, headers, answer = self.request("GET", path_shared, check=404, login="user:userpw")
logging.info("\n*** enable map by owner (json->json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** fetch collection via map (with credentials) as user")
_, headers, answer = self.request("GET", path_shared, check=200, login="user:userpw")
logging.info("\n*** disable map by user (json->json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "disable", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** fetch collection via map (with credentials) as user -> n/a")
_, headers, answer = self.request("GET", path_shared, check=404, login="user:userpw")
logging.info("\n*** delete map by user (json->json) -> fail")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "delete", check=403, login="user:userpw", json_dict=json_dict)
logging.info("\n*** update map by user related to user flags (json->json)")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Enabled'] = True
json_dict['Hidden'] = True
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** list as user and check user flags (json->json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['EnabledByUser'] is True
assert answer_dict['Content'][0]['HiddenByUser'] is True
logging.info("\n*** update map by user related to user flags (json->json)")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Enabled'] = False
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** list as user and check EnabledByUser==False (json->json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['EnabledByUser'] is False
logging.info("\n*** update map by owner related to owner flags (json->json)")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Enabled'] = False
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** list as user and check owner flags (json->json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['EnabledByOwner'] is False
assert answer_dict['Content'][0]['HiddenByOwner'] is False
logging.info("\n*** update map by owner related to owner flag Enabled->True (json->json)")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Enabled'] = True
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** list as user and check owner flags (json->json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['EnabledByOwner'] is True
logging.info("\n*** update map by owner related to Properties -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Properties'] = {"ICAL:calendar-color": "#CCCCCC"}
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** list as user and check Properties (json->json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Properties']["ICAL:calendar-color"] == "#CCCCCC"
logging.info("\n*** update map by user related to Properties -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Properties'] = {"ICAL:calendar-color": "#DDDDDD"}
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** list as user and check Properties (json->json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Properties']["ICAL:calendar-color"] == "#DDDDDD"
logging.info("\n*** delete map by owner (json->json) -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "delete", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
def test_sharing_api_map_update_delete_permissions(self) -> None:
"""share-by-map API usage tests."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"permit_properties_overlay": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"request_content_on_debug": "False"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_shared = "/user/calendarUP-shared-by-owner.ics/"
path_mapped = "/owner/calendarUP.ics/"
path_mapped2 = "/owner/calendarUP2.ics/"
path_mapped_o2 = "/owner2/calendarUP3.ics/"
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
self.mkcalendar(path_mapped2, login="owner:ownerpw")
self.mkcalendar(path_mapped_o2, login="owner2:owner2pw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
logging.info("\n*** create map with PathMapped and User and PathOrToken (json)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** update map by owner: User (json->json) -> 200")
json_dict = {}
json_dict['User'] = "owner"
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by owner: PathMapped (json->json) -> 200")
json_dict = {}
json_dict['PathMapped'] = path_mapped2
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by owner: PathMapped(owner2) (json->json) -> 403")
json_dict = {}
json_dict['PathMapped'] = path_mapped_o2
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "update", check=403, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by owner: PathOrToken (json->json) -> 404")
json_dict = {}
json_dict['PathOrToken'] = path_mapped
_, headers, answer = self._sharing_api_json("map", "update", check=404, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by owner: Permissions without PathOrToken (json->json) -> 400")
json_dict = {}
json_dict['Permissions'] = "rw"
_, headers, answer = self._sharing_api_json("map", "update", check=400, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by owner: Permissions (json->json) -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Permissions'] = "rw"
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by owner: Enabled (json->json) -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Enabled'] = True
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by owner: Hidden (json->json) -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by owner: Properties (json->json) -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Properties'] = {}
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by user: Enabled user-mispatch (json->json) -> 403")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Enabled'] = True
_, headers, answer = self._sharing_api_json("map", "update", check=403, login="user:userpw", json_dict=json_dict)
logging.info("\n*** update map by owner: User (json->json) -> 200")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** update map by user: User (same) (json->json) -> 403")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "update", check=403, login="user:userpw", json_dict=json_dict)
logging.info("\n*** update map by user: PathMapped (json->json) -> 403")
json_dict = {}
json_dict['PathMapped'] = path_shared
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "update", check=403, login="user:userpw", json_dict=json_dict)
logging.info("\n*** update map by user: PathOrToken (json->json) -> 404")
json_dict = {}
json_dict['PathOrToken'] = path_mapped
_, headers, answer = self._sharing_api_json("map", "update", check=404, login="user:userpw", json_dict=json_dict)
logging.info("\n*** update map by user: Permissions (json->json) -> 403")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Permissions'] = "rw"
_, headers, answer = self._sharing_api_json("map", "update", check=403, login="user:userpw", json_dict=json_dict)
logging.info("\n*** update map by user: Enabled (json->json) -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Enabled'] = True
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** update map by user: Hidden (json->json) -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** update map by user: Properties (json->json) -> 200")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Properties'] = {}
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** delete map by user (json->json) -> 403")
json_dict = {}
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "delete", check=403, login="user:userpw", json_dict=json_dict)
logging.info("\n*** delete map by owner (json->json) -> ok")
json_dict = {}
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "delete", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** delete map by owner 2nd time (json->json) -> 404")
json_dict = {}
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "delete", check=404, login="owner:ownerpw", json_dict=json_dict)
def test_sharing_api_map_usercheck(self) -> None:
"""share-by-map API usage tests related to usercheck."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_share1 = "/user1/calendar-shared-by-owner1.ics/"
path_mapped1 = "/owner1/calendar1.ics/"
path_share2 = "/user2/calendar-shared-by-owner2.ics/"
path_mapped2 = "/owner2/calendar2.ics/"
logging.info("\n*** prepare")
self.mkcalendar(path_mapped1, login="%s:%s" % ("owner1", "owner1pw"))
event = get_file_content("event1.ics")
path = path_mapped1 + "/event1.ics"
self.put(path, event, login="%s:%s" % ("owner1", "owner1pw"))
self.mkcalendar(path_mapped2, login="%s:%s" % ("owner2", "owner2pw"))
event = get_file_content("event1.ics")
path = path_mapped2 + "/event1.ics"
self.put(path, event, login="%s:%s" % ("owner2", "owner2pw"))
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
logging.info("\n*** create map user1/owner1 as owner(wrong owner) -> fail")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_mapped1
json_dict['PathOrToken'] = path_share1
_, headers, answer = self._sharing_api_json("map", "create", check=403, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create map user1/owner1:r -> ok")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_mapped1
json_dict['PathOrToken'] = path_share1
json_dict['Permissions'] = "r"
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** create map user1/owner1 (repeat) -> fail")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_mapped1
json_dict['PathOrToken'] = path_share1
_, headers, answer = self._sharing_api_json("map", "create", check=409, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create map user2/owner2:rw -> ok")
json_dict = {}
json_dict['User'] = "user2"
json_dict['PathMapped'] = path_mapped2
json_dict['PathOrToken'] = path_share2
json_dict['Permissions'] = "rw"
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner2:owner2pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** create map user2/owner1 -> 409 (conflict)")
json_dict = {}
json_dict['User'] = "user2"
json_dict['PathMapped'] = path_mapped2
json_dict['PathOrToken'] = path_share1
_, headers, answer = self._sharing_api_json("map", "create", check=409, login="owner2:owner2pw", json_dict=json_dict)
logging.info("\n*** delete map user1 -> ok")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_mapped1
json_dict['PathOrToken'] = path_share1
_, headers, answer = self._sharing_api_json("map", "delete", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** delete map user2 -> ok")
json_dict = {}
json_dict['User'] = "user2"
json_dict['PathMapped'] = path_mapped2
json_dict['PathOrToken'] = path_share2
_, headers, answer = self._sharing_api_json("map", "delete", check=200, login="owner2:owner2pw", json_dict=json_dict)
def test_sharing_api_map_permissions(self) -> None:
"""share-by-map API usage tests related to permissions."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"request_content_on_debug": "False"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_shared_r = "/user/calendar-shared-by-owner-r.ics/"
path_shared_w = "/user1/calendar-shared-by-owner-w.ics/"
path_shared_rw = "/user2/calendar-shared-by-owner-rw.ics/"
path_mapped = "/owner/calendar.ics/"
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
event = get_file_content("event1.ics")
path = path_mapped + "/event1.ics"
self.put(path, event, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check
logging.info("\n*** fetch event as owner (init) -> ok")
_, headers, answer = self.request("GET", path_mapped + "event1.ics", check=200, login="owner:ownerpw")
# create maps
logging.info("\n*** create map user/owner:r -> 400 (Enabled is not boolean)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "r"
json_dict['Enabled'] = "True"
_, headers, answer = self._sharing_api_json("map", "create", check=400, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** create map user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** create map user1/owner:w -> ok")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_w
json_dict['Permissions'] = "w"
json_dict['Enabled'] = True
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** create map user2/owner:rw -> ok")
json_dict = {}
json_dict['User'] = "user2"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_rw
json_dict['Permissions'] = "rw"
json_dict['Enabled'] = True
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# list created maps
logging.info("\n*** list (json->text)")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/csv")
# check permissions, no map is enabled by user -> 404
logging.info("\n*** fetch collection via map:r -> n/a")
_, headers, answer = self.request("GET", path_shared_r, check=404, login="user:userpw")
logging.info("\n*** fetch collection via map:w -> n/a")
_, headers, answer = self.request("GET", path_shared_w, check=404, login="user1:user1pw")
logging.info("\n*** fetch collection via map:rw -> n/a")
_, headers, answer = self.request("GET", path_shared_rw, check=404, login="user2:user2pw")
# enable maps by user
logging.info("\n*** enable map by user:r")
json_dict = {}
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** enable map by user1:w")
json_dict = {}
json_dict['PathOrToken'] = path_shared_w
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user1:user1pw", json_dict=json_dict)
logging.info("\n*** enable map by user2:rw")
json_dict = {}
json_dict['PathOrToken'] = path_shared_rw
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user2:user2pw", json_dict=json_dict)
# list adjusted maps
logging.info("\n*** list (json->text)")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/csv")
# check permissions, no map is enabled by user -> 404
logging.info("\n*** fetch collection via map:r -> ok")
_, headers, answer = self.request("GET", path_shared_r, check=200, login="user:userpw")
logging.info("\n*** fetch collection via map:w -> fail")
_, headers, answer = self.request("GET", path_shared_w, check=403, login="user1:user1pw")
logging.info("\n*** fetch collection via map:rw -> ok")
_, headers, answer = self.request("GET", path_shared_rw, check=200, login="user2:user2pw")
# list adjusted maps
logging.info("\n*** list (json->text)")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/csv")
# PUT
logging.info("\n*** put to collection by user via map:r -> fail")
event = get_file_content("event2.ics")
path = path_shared_r + "/event2.ics"
self.put(path, event, check=403, login="user:userpw")
logging.info("\n*** put to collection by user1 via map:w -> ok")
event = get_file_content("event2.ics")
path = path_shared_w + "event2.ics"
self.put(path, event, check=201, login="user1:user1pw")
# check result
logging.info("\n*** fetch event via map:r -> ok")
_, headers, answer = self.request("GET", path_shared_r + "event2.ics", check=200, login="user:userpw")
logging.info("\n*** fetch event as owner -> ok")
_, headers, answer = self.request("GET", path_mapped + "event2.ics", check=200, login="owner:ownerpw")
logging.info("\n*** put to collection by user2 via map:rw -> ok")
event = get_file_content("event3.ics")
path = path_shared_rw + "event3.ics"
self.put(path, event, check=201, login="user2:user2pw")
# check result
logging.info("\n*** fetch event via map:r -> ok")
_, headers, answer = self.request("GET", path_shared_r + "event2.ics", check=200, login="user:userpw")
logging.info("\n*** fetch event via map:r -> ok")
_, headers, answer = self.request("GET", path_shared_r + "event3.ics", check=200, login="user:userpw")
logging.info("\n*** fetch event via map:rw -> ok")
_, headers, answer = self.request("GET", path_shared_rw + "event2.ics", check=200, login="user2:user2pw")
logging.info("\n*** fetch event via map:rw -> ok")
_, headers, answer = self.request("GET", path_shared_rw + "event3.ics", check=200, login="user2:user2pw")
logging.info("\n*** fetch event as owner -> ok")
_, headers, answer = self.request("GET", path_mapped + "event1.ics", check=200, login="owner:ownerpw")
logging.info("\n*** fetch event as owner -> ok")
_, headers, answer = self.request("GET", path_mapped + "event2.ics", check=200, login="owner:ownerpw")
logging.info("\n*** fetch event as owner -> ok")
_, headers, answer = self.request("GET", path_mapped + "event3.ics", check=200, login="owner:ownerpw")
# DELETE
logging.info("\n*** DELETE from collection by user via map:r -> fail")
_, headers, answer = self.request("DELETE", path_shared_r + "event1.ics", check=403, login="user:userpw")
logging.info("\n*** DELETE from collection by user via map:rw -> ok")
_, headers, answer = self.request("DELETE", path_shared_rw + "event2.ics", check=200, login="user2:user2pw")
logging.info("\n*** DELETE from collection by user via map:w -> ok")
_, headers, answer = self.request("DELETE", path_shared_w + "event3.ics", check=200, login="user1:user1pw")
# check results
logging.info("\n*** fetch event as owner -> ok")
_, headers, answer = self.request("GET", path_mapped + "event1.ics", check=200, login="owner:ownerpw")
logging.info("\n*** fetch event as owner -> fail")
_, headers, answer = self.request("GET", path_mapped + "event2.ics", check=404, login="owner:ownerpw")
logging.info("\n*** fetch event as owner -> fail")
_, headers, answer = self.request("GET", path_mapped + "event3.ics", check=404, login="owner:ownerpw")
def test_sharing_api_map_report_access(self) -> None:
"""share-by-map API usage tests related to report."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_shared = "/user/calendar2-shared-by-owner.ics/"
path_mapped = "/owner/calendar2.ics/"
path_shared_item = os.path.join(path_shared, "event1.ics")
path_mapped_item = os.path.join(path_mapped, "event1.ics")
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
event = get_file_content("event1.ics")
self.put(path_mapped_item, event, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check GET
logging.info("\n*** GET event as owner (init) -> ok")
_, headers, answer = self.request("GET", path_mapped + "event1.ics", check=200, login="owner:ownerpw")
# check REPORT as owner
logging.info("\n*** REPORT collection owner -> ok")
_, responses = self.report(path_mapped, """\
<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop xmlns:D="DAV:">
<D:getetag />
</D:prop>
</C:calendar-query>""", login="owner:ownerpw")
assert len(responses) == 1
logging.info("response: %r", responses)
response = responses[path_mapped_item]
assert isinstance(response, dict)
status, prop = response["D:getetag"]
assert status == 200 and prop.text
# create map
logging.info("\n*** create map user/owner -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# check REPORT as user
logging.info("\n*** REPORT collection user -> 404")
_, responses = self.report(path_shared, """\
<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop xmlns:D="DAV:">
<D:getetag />
</D:prop>
</C:calendar-query>""", login="user:userpw", check=404)
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# check REPORT as user
logging.info("\n*** REPORT collection user -> ok")
_, responses = self.report(path_shared, """\
<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop xmlns:D="DAV:">
<D:getetag />
</D:prop>
</C:calendar-query>""", login="user:userpw")
assert len(responses) == 1
logging.info("response: %r", responses)
response = responses[path_shared_item]
assert isinstance(response, dict)
status, prop = response["D:getetag"]
assert status == 200 and prop.text
def test_sharing_api_map_hidden(self) -> None:
"""share-by-map API usage tests related to report checking hidden."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_user_base = "/user/"
path_mapped_base = "/owner/"
path_user = path_user_base + "calendarRH.ics/"
path_mapped = path_mapped_base + "calendarRH.ics/"
path_mapped2 = path_mapped_base + "calendarRH2.ics/"
path_shared = path_user_base + "calendarRH-shared-by-owner.ics/"
path_mapped_item = os.path.join(path_mapped, "event1.ics")
path_user_item = os.path.join(path_user, "event2.ics")
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
event = get_file_content("event1.ics")
self.put(path_mapped_item, event, login="owner:ownerpw")
self.mkcalendar(path_mapped2, login="owner:ownerpw")
self.mkcalendar(path_user, login="user:userpw")
event = get_file_content("event2.ics")
self.put(path_user_item, event, login="user:userpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check GET
logging.info("\n*** GET event1 as owner -> 200")
_, headers, answer = self.request("GET", path_mapped_item, check=200, login="owner:ownerpw")
logging.info("\n*** GET event2 as user -> 200")
_, headers, answer = self.request("GET", path_user_item, check=200, login="user:userpw")
logging.info("\n*** GET collections as user -> 403")
_, headers, answer = self.request("GET", path_user_base, check=403, login="user:userpw")
# check PROPFIND as user
logging.info("\n*** PROPFIND collection user -> ok")
_, responses = self.propfind(path_user_base, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<calendar-home-set xmlns="urn:ietf:params:xml:ns:caldav" />
</propfind>""", login="user:userpw", HTTP_DEPTH="1")
assert len(responses) == 2
logging.info("response: %r", responses)
response = responses[path_user]
assert isinstance(response, dict)
# create map
logging.info("\n*** create map user/owner -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped_base, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<calendar-home-set xmlns="urn:ietf:params:xml:ns:caldav" />
</propfind>""", login="owner:ownerpw", HTTP_DEPTH="1")
assert len(responses) == 3
logging.info("response: %r", responses)
response = responses[path_mapped]
assert isinstance(response, dict)
response = responses[path_mapped2]
assert isinstance(response, dict)
# check PROPFIND as user
logging.info("\n*** PROPFIND collection user -> ok")
_, responses = self.propfind(path_user_base, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<calendar-home-set xmlns="urn:ietf:params:xml:ns:caldav" />
</propfind>""", login="user:userpw", HTTP_DEPTH="1")
assert len(responses) == 2
logging.info("response: %r", responses)
response = responses[path_user]
assert isinstance(response, dict)
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# check PROPFIND as user
logging.info("\n*** PROPFIND collection user -> ok")
_, responses = self.propfind(path_user_base, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<calendar-home-set xmlns="urn:ietf:params:xml:ns:caldav" />
</propfind>""", login="user:userpw", HTTP_DEPTH="1")
assert len(responses) == 2
logging.info("response: %r", responses)
response = responses[path_user]
assert isinstance(response, dict)
# unhide map by user
logging.info("\n*** unhide map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "unhide", check=200, login="user:userpw", json_dict=json_dict)
# check PROPFIND as user
logging.info("\n*** PROPFIND collection user -> ok (now 3 items)")
_, responses = self.propfind(path_user_base, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<calendar-home-set xmlns="urn:ietf:params:xml:ns:caldav" />
</propfind>""", login="user:userpw", HTTP_DEPTH="1")
assert len(responses) == 3
logging.info("response: %r", responses)
response = responses[path_user]
assert isinstance(response, dict)
response = responses[path_shared]
assert isinstance(response, dict)
def test_sharing_api_map_propfind_base(self) -> None:
"""share-by-map API usage tests related to propfind."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_shared = "/user/calendar-shared-by-owner.ics/"
path_mapped = "/owner/calendar.ics/"
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
event = get_file_content("event1.ics")
path = os.path.join(path_mapped, "event1.ics")
self.put(path, event, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check GET
logging.info("\n*** GET event as owner (init) -> ok")
_, headers, answer = self.request("GET", path_mapped + "event1.ics", check=200, login="owner:ownerpw")
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["D:current-user-principal"]
assert status == 200 and len(prop) == 1
element = prop.find(xmlutils.make_clark("D:href"))
assert element is not None and element.text == "/owner/"
# create map
logging.info("\n*** create map user/owner -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# check PROPFIND as user
logging.info("\n*** PROPFIND collection user -> 404")
_, responses = self.propfind(path_shared, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="user:userpw", check=404)
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# check PROPFIND as user
logging.info("\n*** PROPFIND collection user -> ok")
_, responses = self.propfind(path_shared, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="user:userpw", check=207)
logging.info("response: %r", responses)
response = responses[path_shared]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["D:current-user-principal"]
assert status == 200 and len(prop) == 1
element = prop.find(xmlutils.make_clark("D:href"))
assert element is not None and element.text == "/user/"
def test_sharing_api_map_proppatch_acl(self) -> None:
"""share-by-map API usage tests related to report."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_mapped = "/owner/calendarPP.ics/"
path_shared_r = "/user/calendarPP-shared-by-owner-r.ics/"
path_shared_w = "/user1/calendarPP-shared-by-owner-w.ics/"
path_shared_rw = "/user2/calendarPP-shared-by-owner-rw.ics/"
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
event = get_file_content("event1.ics")
path = os.path.join(path_mapped, "event1.ics")
self.put(path, event, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check GET
logging.info("\n*** GET event as owner (init) -> ok")
_, headers, answer = self.request("GET", path_mapped + "event1.ics", check=200, login="owner:ownerpw")
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["D:current-user-principal"]
assert status == 200 and len(prop) == 1
element = prop.find(xmlutils.make_clark("D:href"))
assert element is not None and element.text == "/owner/"
# check PROPPATCH as owner
logging.info("\n*** PROPPATCH collection owner -> ok")
proppatch = get_file_content("proppatch_set_calendar_color.xml")
_, responses = self.proppatch(path_mapped, proppatch, login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["ICAL:calendar-color"]
assert status == 200 and not prop.text
# check PROPPATCH as user
logging.info("\n*** PROPPATCH collection as user -> 404")
proppatch = get_file_content("proppatch_remove_calendar_color.xml")
_, responses = self.proppatch(path_shared_r, proppatch, login="user:userpw", check=404)
_, responses = self.proppatch(path_shared_w, proppatch, login="user1:user1pw", check=404)
_, responses = self.proppatch(path_shared_rw, proppatch, login="user2:user2pw", check=404)
# create map
logging.info("\n*** create map user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** create map user1/owner:w -> ok")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_w
json_dict['Permissions'] = "w"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** create map user2/owner:rw -> ok")
json_dict = {}
json_dict['User'] = "user2"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_rw
json_dict['Permissions'] = "rw"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# check PROPPATCH as user
logging.info("\n*** PROPPATCH collection as user -> 403")
proppatch = get_file_content("proppatch_set_calendar_color.xml")
_, responses = self.proppatch(path_shared_r, proppatch, login="user:userpw", check=404)
_, responses = self.proppatch(path_shared_w, proppatch, login="user1:user1pw", check=404)
_, responses = self.proppatch(path_shared_rw, proppatch, login="user2:user2pw", check=404)
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** enable map by user1")
json_dict = {}
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_w
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user1:user1pw", json_dict=json_dict)
logging.info("\n*** enable map by user2")
json_dict = {}
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_rw
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user2:user2pw", json_dict=json_dict)
# check PROPPATCH as user
proppatch = get_file_content("proppatch_remove_calendar_color.xml")
logging.info("\n*** PROPPATCH collection as user:r -> 403")
_, responses = self.proppatch(path_shared_r, proppatch, login="user:userpw", check=403)
logging.info("\n*** PROPPATCH collection as user:w -> ok")
_, responses = self.proppatch(path_shared_w, proppatch, login="user1:user1pw")
logging.info("response: %r", responses)
logging.info("\n*** PROPPATCH collection as user:rw -> ok")
_, responses = self.proppatch(path_shared_rw, proppatch, login="user2:user2pw")
logging.info("response: %r", responses)
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["D:current-user-principal"]
assert status == 200 and len(prop) == 1
element = prop.find(xmlutils.make_clark("D:href"))
assert element is not None and element.text == "/owner/"
assert "ICAL:calendar-color" not in response
def test_sharing_api_map_move(self) -> None:
"""share-by-map API usage tests related to MOVE."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_user = "/user/calendarM.ics/"
path_mapped1 = "/owner/calendar1M.ics/"
path_mapped1r = "/owner/calendar1MR.ics/"
path_mapped2 = "/owner/calendar2M.ics/"
path_shared1_r = "/user/calendar1M-shared-by-owner-r.ics/"
path_shared1_rw = "/user/calendar1M-shared-by-owner-rw.ics/"
path_shared2_rw = "/user/calendar2M-shared-by-owner-rw.ics/"
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped1, login="owner:ownerpw")
event = get_file_content("event1.ics")
self.put(os.path.join(path_mapped1, "event1.ics"), event, login="owner:ownerpw")
self.mkcalendar(path_mapped1r, login="owner:ownerpw")
self.mkcalendar(path_mapped2, login="owner:ownerpw")
event = get_file_content("event2.ics")
self.put(os.path.join(path_mapped2, "event2.ics"), event, login="owner:ownerpw")
self.mkcalendar(path_user, login="user:userpw")
event = get_file_content("event3.ics")
self.put(os.path.join(path_user, "event3.ics"), event, login="user:userpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check GET as owner
logging.info("\n*** GET mapped1/event1 as owner (init) -> ok")
_, headers, answer = self.request("GET", os.path.join(path_mapped1, "event1.ics"), check=200, login="owner:ownerpw")
logging.info("\n*** GET mapped2/event2 as owner (init) -> ok")
_, headers, answer = self.request("GET", os.path.join(path_mapped2, "event2.ics"), check=200, login="owner:ownerpw")
# check MOVE as owner
logging.info("\n*** MOVE event1 to mapped2 as owner -> ok")
self.request("MOVE", os.path.join(path_mapped1, "event1.ics"), check=201,
login="owner:ownerpw",
HTTP_DESTINATION="http://127.0.0.1/"+os.path.join(path_mapped2, "event1.ics"))
logging.info("\n*** GET mapped2/event1 as owner (after move) -> ok")
_, headers, answer = self.request("GET", os.path.join(path_mapped2, "event1.ics"), check=200, login="owner:ownerpw")
# check GET as user
logging.info("\n*** GET event1 as user -> 404")
_, headers, answer = self.request("GET", os.path.join(path_shared1_r, "event1.ics"), check=404, login="user:userpw")
logging.info("\n*** GET event2 as user -> 404")
_, headers, answer = self.request("GET", os.path.join(path_shared2_rw, "event2.ics"), check=404, login="user:userpw")
# create map
logging.info("\n*** create map user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped1r
json_dict['PathOrToken'] = path_shared1_r
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** create map user/owner:rw -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped1
json_dict['PathOrToken'] = path_shared1_rw
json_dict['Permissions'] = "rw"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** create map user/owner:rw -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped2
json_dict['PathOrToken'] = path_shared2_rw
json_dict['Permissions'] = "rw"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# check MOVE as user
logging.info("\n*** MOVE event1 of shared1 to shared2 as user -> 404 (not enabled)")
self.request("MOVE", os.path.join(path_shared1_r, "event1.ics"), check=404,
login="user:userpw",
HTTP_DESTINATION="http://127.0.0.1/"+os.path.join(path_shared2_rw, "event1.ics"))
logging.info("\n*** MOVE event1 of shared2 to shared1 as user -> 404 (not enabled)")
self.request("MOVE", os.path.join(path_shared2_rw, "event1.ics"), check=404,
login="user:userpw",
HTTP_DESTINATION="http://127.0.0.1/"+os.path.join(path_shared1_r, "event1.ics"))
# enable map by user
logging.info("\n*** enable map shared1_r by user")
json_dict = {}
json_dict['PathMapped'] = path_mapped1r
json_dict['PathOrToken'] = path_shared1_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** enable map shared1_rw by user")
json_dict = {}
json_dict['PathMapped'] = path_mapped1
json_dict['PathOrToken'] = path_shared1_rw
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** enable map shared2_rw by user")
json_dict = {}
json_dict['PathMapped'] = path_mapped2
json_dict['PathOrToken'] = path_shared2_rw
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# check GET as user
logging.info("\n*** GET event1 as user -> 404")
_, headers, answer = self.request("GET", os.path.join(path_shared1_r, "event1.ics"), check=404, login="user:userpw")
logging.info("\n*** GET event1 as user -> 404")
_, headers, answer = self.request("GET", os.path.join(path_shared1_rw, "event1.ics"), check=404, login="user:userpw")
logging.info("\n*** GET event1 as user -> ok")
_, headers, answer = self.request("GET", os.path.join(path_shared2_rw, "event1.ics"), check=200, login="user:userpw")
# check MOVE as user between shares
logging.info("\n*** MOVE event1 of shared1_r to shared2_rw as user -> 403 (not permitted to move from r)")
self.request("MOVE", os.path.join(path_shared1_r, "event1.ics"), check=403,
login="user:userpw",
HTTP_DESTINATION="http://127.0.0.1/"+os.path.join(path_shared2_rw, "event1.ics"))
logging.info("\n*** MOVE event1 of shared2_rw to shared1_r as user -> 403 (not permitted to move to r)")
self.request("MOVE", os.path.join(path_shared2_rw, "event1.ics"), check=403,
login="user:userpw",
HTTP_DESTINATION="http://127.0.0.1/"+os.path.join(path_shared1_r, "event1.ics"))
logging.info("\n*** MOVE event1 of shared1_rw to shared2_rw as user -> 404 (already moved by owner)")
self.request("MOVE", os.path.join(path_shared1_rw, "event1.ics"), check=404,
login="user:userpw",
HTTP_DESTINATION="http://127.0.0.1/"+os.path.join(path_shared2_rw, "event1.ics"))
logging.info("\n*** MOVE event1 of shared2_rw to shared1_rw as user -> 201")
self.request("MOVE", os.path.join(path_shared2_rw, "event1.ics"), check=201,
login="user:userpw",
HTTP_DESTINATION="http://127.0.0.1/"+os.path.join(path_shared1_rw, "event1.ics"))
# check GET as user
logging.info("\n*** GET event1 from r as user -> 404")
_, headers, answer = self.request("GET", os.path.join(path_shared1_r, "event1.ics"), check=404, login="user:userpw")
logging.info("\n*** GET event1 from 1/rw as user -> ok")
_, headers, answer = self.request("GET", os.path.join(path_shared1_rw, "event1.ics"), check=200, login="user:userpw")
logging.info("\n*** GET event1 from 2/rw as user -> 404")
_, headers, answer = self.request("GET", os.path.join(path_shared2_rw, "event1.ics"), check=404, login="user:userpw")
# check MOVE as user between shares and own calendar
logging.info("\n*** GET event3 as user -> 200")
_, headers, answer = self.request("GET", os.path.join(path_user, "event3.ics"), check=200, login="user:userpw")
logging.info("\n*** GET event3 as user from shared2_rw -> 404")
_, headers, answer = self.request("GET", os.path.join(path_shared2_rw, "event3.ics"), check=404, login="user:userpw")
logging.info("\n*** MOVE event3 of own to shared2_rw as user -> 201")
self.request("MOVE", os.path.join(path_user, "event3.ics"), check=201,
login="user:userpw",
HTTP_DESTINATION="http://127.0.0.1/"+os.path.join(path_shared2_rw, "event3.ics"))
logging.info("\n*** GET event3 as user from shared2_rw -> 200")
_, headers, answer = self.request("GET", os.path.join(path_shared2_rw, "event3.ics"), check=200, login="user:userpw")
logging.info("\n*** GET event3 as user -> 404")
_, headers, answer = self.request("GET", os.path.join(path_user, "event3.ics"), check=404, login="user:userpw")
logging.info("\n*** MOVE event3 to own from shared2_rw as user -> 201")
self.request("MOVE", os.path.join(path_shared2_rw, "event3.ics"), check=201,
login="user:userpw",
HTTP_DESTINATION="http://127.0.0.1/"+os.path.join(path_user, "event3.ics"))
def test_sharing_api_update(self) -> None:
"""sharing API usage tests related to update."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "False",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
form_array: Sequence[str]
json_dict: dict
path_mapped1 = "/owner/calendar1U.ics/"
path_mapped2 = "/owner/calendar2U.ics/"
path_shared1 = "/user/calendar1U-shared-by-owner.ics/"
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped1, login="owner:ownerpw")
event = get_file_content("event1.ics")
self.put(os.path.join(path_mapped1, "event1.ics"), event, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check GET as owner
logging.info("\n*** GET mapped1 as owner (init) -> 200")
_, headers, answer = self.request("GET", path_mapped1, check=200, login="owner:ownerpw")
logging.info("\n*** GET shared1 as user (init) -> 404")
_, headers, answer = self.request("GET", path_shared1, check=404, login="user:userpw")
logging.info("\n*** create map user/owner:w -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped1
json_dict['PathOrToken'] = path_shared1
json_dict['Permissions'] = "w"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** GET shared1 as user (still not enabled by user) -> 404")
_, headers, answer = self.request("GET", path_shared1, check=404, login="user:userpw")
# enable map by user
logging.info("\n*** enable map shared1 by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped1
json_dict['PathOrToken'] = path_shared1
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** list/all (form->csv)")
form_array = []
_, headers, answer = self._sharing_api_form("map", "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "Lines=1" in answer
# read collection
logging.info("\n*** GET shared1 as user (no read permissions set by owner) -> 403")
_, headers, answer = self.request("GET", path_shared1, check=403, login="user:userpw")
# update map
logging.info("\n*** update map user/owner:r -> ok")
json_dict = {}
json_dict['PathMapped'] = path_mapped1
json_dict['PathOrToken'] = path_shared1
json_dict['Permissions'] = "r"
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** list/all (form->csv)")
form_array = []
_, headers, answer = self._sharing_api_form("map", "list", check=200, login="owner:ownerpw", form_array=form_array)
assert "Status='success'" in answer
assert "Lines=1" in answer
# read collection
logging.info("\n*** GET shared1 as user (read permissions set by owner) -> 200")
_, headers, answer = self.request("GET", path_shared1, check=200, login="user:userpw")
# update map
logging.info("\n*** update map user/owner:path_mapped2 -> ok")
json_dict = {}
json_dict['PathMapped'] = path_mapped2
json_dict['PathOrToken'] = path_shared1
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# read collection
logging.info("\n*** GET shared1 as user (path not matching) -> 404")
_, headers, answer = self.request("GET", path_shared1, check=404, login="user:userpw")
logging.info("\n*** create mapped2 collection")
self.mkcalendar(path_mapped2, login="owner:ownerpw")
event = get_file_content("event2.ics")
self.put(os.path.join(path_mapped2, "event2.ics"), event, login="owner:ownerpw")
# read collection
logging.info("\n*** GET shared1 as user (path now matching) -> 200")
_, headers, answer = self.request("GET", path_shared1, check=200, login="user:userpw")
# cleanup
self.delete(path_mapped2, login="owner:ownerpw")
def test_sharing_api_list_filter(self) -> None:
"""sharing API usage tests related to update."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "False",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_user1 = "/user1/calendarLFu1.ics/"
path_user2 = "/user2/calendarLFu2.ics/"
path_user1_shared1 = "/user1/calendarLFo1-shared.ics/"
path_user1_shared2 = "/user1/calendarLFo2-shared.ics/"
path_user2_shared1 = "/user2/calendarLFo1-shared.ics/"
path_owner1 = "/owner1/calendarLFo1.ics/"
path_owner2 = "/owner2/calendarLFo2.ics/"
logging.info("\n*** prepare")
self.mkcalendar(path_owner1, login="owner1:owner1pw")
self.mkcalendar(path_owner2, login="owner2:owner2pw")
self.mkcalendar(path_user1, login="user1:user1pw")
self.mkcalendar(path_user2, login="user2:user2pw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# list current
logging.info("\n*** list owner1 -> empty")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "not-found"
logging.info("\n*** list owner2 -> empty")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner2:owner2pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "not-found"
logging.info("\n*** list user1 -> empty")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user1:user1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "not-found"
logging.info("\n*** list user2 -> empty")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user2:user2pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "not-found"
# create map#1
logging.info("\n*** create map user1/owner1 -> ok")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_owner1
json_dict['PathOrToken'] = path_user1_shared1
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** list owner1")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
logging.info("\n*** list user1")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user1:user1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
# create map#2
logging.info("\n*** create map user1/owner2 -> ok")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_owner2
json_dict['PathOrToken'] = path_user1_shared2
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner2:owner2pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** list user1 -> 2 entries")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user1:user1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 2
# create map#3
logging.info("\n*** create map user2/owner1 -> ok")
json_dict = {}
json_dict['User'] = "user2"
json_dict['PathMapped'] = path_owner1
json_dict['PathOrToken'] = path_user2_shared1
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** list owner1 -> 2 entries")
json_dict = {}
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 2
logging.info("\n*** list user1 filter for PathMapped -> 1 entries")
json_dict = {}
json_dict['PathMapped'] = path_owner1
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user1:user1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
logging.info("\n*** list user1 filter for PathShared -> 1 entries")
json_dict = {}
json_dict['PathOrToken'] = path_user1_shared1
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user1:user1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
def test_sharing_api_create_conflict(self) -> None:
"""sharing API usage tests related to conflicts."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "False",
"rights_rule_doesnt_match_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
rights_file_path = os.path.join(self.colpath, "rights")
with open(rights_file_path, "w") as f:
f.write("""\
[default-collection]
user: .+
collection: .+
permissions: RrWw
[default]
user: .+
collection: {user}(/.*)?
permissions: RrWw""")
self.configure({"rights": {"file": rights_file_path}})
json_dict: dict
path_user1 = "/user1/calendarCCu1.ics/"
path_user2 = "/user2/calendarCCu2.ics/"
path_user1_shared1 = "/user1/calendarCCo1-shared.ics/"
path_user2_shared1 = "/user2/calendarCCo1-shared.ics/"
path_owner1 = "/owner1/calendarCCo1.ics/"
path_owner2 = "/owner2/calendarCCo2.ics/"
logging.info("\n*** prepare")
self.mkcalendar(path_owner1, login="owner1:owner1pw")
self.mkcalendar(path_owner2, login="owner2:owner2pw")
self.mkcalendar(path_user1, login="user1:user1pw")
self.mkcalendar(path_user2, login="user2:user2pw")
# create calendar a 2nd time
logging.info("\n*** mkcalendar user2 -> conflict")
self.mkcalendar(path_user2, login="user2:user2pw", check=409)
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# owner_only
self.configure({"rights": {"type": "owner_only"}})
# create map
logging.info("\n*** create map user1/owner1 -> ok")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_owner1
json_dict['PathOrToken'] = path_user1_shared1
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** mkcalendar as user1 for user1/shared1 -> conflict")
self.mkcalendar(path_user1_shared1, login="user1:user1pw", check=409)
# create map
logging.info("\n*** create map user2/owner1 -> ok")
json_dict = {}
json_dict['User'] = "user2"
json_dict['PathMapped'] = path_owner1
json_dict['PathOrToken'] = path_user2_shared1
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** mkcol as user2 for user2/shared1 -> conflict")
self.mkcalendar(path_user2_shared1, login="user2:user2pw", check=409)
# create map
logging.info("\n*** create map user2/owner2 -> 409")
json_dict = {}
json_dict['User'] = "user2"
json_dict['PathMapped'] = path_owner1
json_dict['PathOrToken'] = path_user2
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=409, login="owner1:owner1pw", json_dict=json_dict)
# from_file
self.configure({"rights": {"type": "from_file"}})
logging.info("\n*** mkcalendar as user1 for user2/shared1 with rights from file -> conflict")
self.mkcalendar(path_user2_shared1, login="user1:user1pw", check=409)
logging.info("\n*** mkcol as user1 for user2/shared1 with rights from file -> conflict")
self.mkcol(path_user2_shared1, login="user1:user1pw", check=409)
def test_sharing_api_permissions_global(self) -> None:
"""sharing API usage tests related to global permissions."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "False",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_user1 = "/user1/calendarPGu1.ics/"
path_owner1 = "/owner1/calendarPGo1.ics/"
logging.info("\n*** prepare")
self.mkcalendar(path_owner1, login="owner1:owner1pw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# create map
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_owner1
json_dict['PathOrToken'] = path_user1
logging.info("\n*** create map user1/owner1 but globally disabled -> 403")
self.configure({"sharing": {"permit_create_map": "False"}})
_, headers, answer = self._sharing_api_json("map", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create map user1/owner1 but globally enabled -> 200")
self.configure({"sharing": {"permit_create_map": "True"}})
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
# create token
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_owner1
logging.info("\n*** create token owner1 but globally disabled -> 403")
self.configure({"sharing": {"permit_create_token": "False"}})
_, headers, answer = self._sharing_api_json("token", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create token owner1 but globally enabled -> 200")
self.configure({"sharing": {"permit_create_token": "True"}})
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
def test_sharing_api_permissions_rights(self) -> None:
"""sharing API usage tests related to rights permissions."""
rights_file_path = os.path.join(self.colpath, "rights")
with open(rights_file_path, "w") as f:
f.write("""\
[default-collection]
user: .+
collection: {user}
permissions: RrWw
[owner1-T]
user: owner1
collection: {user}/cal-T-uc(/.*)?
permissions: RrWwT
[owner1-t]
user: owner1
collection: {user}/cal-t-lc(/.*)?
permissions: RrWwt
[owner1-M]
user: owner1
collection: {user}/cal-M-uc(/.*)?
permissions: RrWwM
[owner1-m]
user: owner1
collection: {user}/cal-m-lc(/.*)?
permissions: RrWwm
[default]
user: .+
collection: {user}(/.*)?
permissions: RrWw""")
self.configure({"rights": {"file": rights_file_path}})
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "False",
"rights_rule_doesnt_match_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "from_file"}})
json_dict: dict
path_user1 = "/user1/calendarPGu1.ics/"
path_owner1_T = "/owner1/cal-T-uc/"
path_owner1_t = "/owner1/cal-t-lc/"
path_owner1_M = "/owner1/cal-M-uc/"
path_owner1_m = "/owner1/cal-m-lc/"
logging.info("\n*** prepare")
self.mkcalendar(path_owner1_T, login="owner1:owner1pw")
self.mkcalendar(path_owner1_t, login="owner1:owner1pw")
self.mkcalendar(path_owner1_M, login="owner1:owner1pw")
self.mkcalendar(path_owner1_m, login="owner1:owner1pw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# create map
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathOrToken'] = path_user1
logging.info("\n*** create map user1/owner1, globally disabled")
self.configure({"sharing": {"permit_create_map": "False"}})
logging.info("\n*** create map user1/owner1, globally disabled / not granted M -> 403")
json_dict['PathMapped'] = path_owner1_M
json_dict['PathOrToken'] = path_user1.replace(".ics", "dM-uc" + db_type + ".ics")
_, headers, answer = self._sharing_api_json("map", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create map user1/owner1, globally disabled / not granted T -> 403")
json_dict['PathMapped'] = path_owner1_T
json_dict['PathOrToken'] = path_user1.replace(".ics", "dT-uc" + db_type + ".ics")
_, headers, answer = self._sharing_api_json("map", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create map user1/owner1, globally disabled / not granted t -> 403")
json_dict['PathMapped'] = path_owner1_t
json_dict['PathOrToken'] = path_user1.replace(".ics", "dt-lc" + db_type + ".ics")
_, headers, answer = self._sharing_api_json("map", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create map user1/owner1, globally disabled / granted m -> 200")
json_dict['PathMapped'] = path_owner1_m
json_dict['PathOrToken'] = path_user1.replace(".ics", "dm-lc" + db_type + ".ics")
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** deletee map user1/owner1, globally disabled / granted m -> 200")
json_dict['PathMapped'] = path_owner1_m
json_dict['PathOrToken'] = path_user1.replace(".ics", "dm-lc" + db_type + ".ics")
_, headers, answer = self._sharing_api_json("map", "delete", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create map user1/owner1, globally enabled")
self.configure({"sharing": {"permit_create_map": "True"}})
logging.info("\n*** create map user1/owner1, globally enabled / not granted M -> 403")
json_dict['PathMapped'] = path_owner1_M
json_dict['PathOrToken'] = path_user1.replace(".ics", "eM-uc" + db_type + ".ics")
_, headers, answer = self._sharing_api_json("map", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create map user1/owner1, globally enabled / ignore T -> 200")
json_dict['PathMapped'] = path_owner1_T
json_dict['PathOrToken'] = path_user1.replace(".ics", "eT-uc" + db_type + ".ics")
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create map user1/owner1, globally enabled / ignore t -> 200")
json_dict['PathMapped'] = path_owner1_t
json_dict['PathOrToken'] = path_user1.replace(".ics", "et-lc" + db_type + ".ics")
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create map user1/owner1, globally enabled / ignore m -> 200")
json_dict['PathMapped'] = path_owner1_m
json_dict['PathOrToken'] = path_user1.replace(".ics", "em-lc" + db_type + ".ics")
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
# create token
json_dict = {}
logging.info("\n*** create token owner1, globally disabled")
self.configure({"sharing": {"permit_create_token": "False"}})
logging.info("\n*** create token owner1, globally disabled / not granted M -> 403")
json_dict['PathMapped'] = path_owner1_M
_, headers, answer = self._sharing_api_json("token", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create token owner1, globally disabled / not granted m -> 403")
json_dict['PathMapped'] = path_owner1_m
_, headers, answer = self._sharing_api_json("token", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create token owner1, globally disabled / not granted T -> 403")
json_dict['PathMapped'] = path_owner1_T
_, headers, answer = self._sharing_api_json("token", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create token owner1, globally disabled / granted t -> 200")
json_dict['PathMapped'] = path_owner1_t
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create token owner1, globally enabled")
self.configure({"sharing": {"permit_create_token": "True"}})
logging.info("\n*** create token owner1, globally enabled / ignore M -> 200")
json_dict['PathMapped'] = path_owner1_M
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create token owner1, globally enabled / ignore m -> 200")
json_dict['PathMapped'] = path_owner1_m
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create token owner1, globally enabled / not granted T -> 403")
json_dict['PathMapped'] = path_owner1_T
_, headers, answer = self._sharing_api_json("token", "create", check=403, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** create token owner1, globally enabled / ignore t -> 200")
json_dict['PathMapped'] = path_owner1_t
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
def test_sharing_api_permissions_default(self) -> None:
"""sharing API usage tests related to global permissions."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_owner1 = "/owner1/calendarPGo1.ics/"
path_owner1_rw = "/owner1/calendarPGo1rw.ics/"
path_owner1_RrWw = "/owner1/calendarPGo1RrWw.ics/"
path_user1_r = "/user1/calendarPGu1-r.ics/"
path_user1_rw = "/user1/calendarPGu1-rw.ics/"
path_user1_RrWw = "/user1/calendarPGu1-RrWw.ics/"
logging.info("\n*** prepare")
self.mkcalendar(path_owner1, login="owner1:owner1pw")
self.mkcalendar(path_owner1_rw, login="owner1:owner1pw")
self.mkcalendar(path_owner1_RrWw, login="owner1:owner1pw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# create map
self.configure({"sharing": {"default_permissions_create_map": "r"}})
logging.info("\n*** create map user1/owner1 r -> 200")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_owner1
json_dict['PathOrToken'] = path_user1_r
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
# enable map by user
logging.info("\n*** enable map by user1")
json_dict = {}
json_dict['PathOrToken'] = path_user1_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user1:user1pw", json_dict=json_dict)
logging.info("\n*** list (json->json)")
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Permissions'] == "r"
# check PROPFIND/priviledges item as user
logging.info("\n*** PROPFIND/priviledges item as user")
priviledges_list = self._propfind_priviledges(path_user1_r, login="user1:user1pw")
assert "D:read" in priviledges_list
assert "D:write-content" not in priviledges_list
assert "D:write-properties" not in priviledges_list
assert "D:write" not in priviledges_list
assert "D:all" not in priviledges_list
logging.info("\n*** create map user1/owner1 rw -> 200")
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_owner1_rw
json_dict['PathOrToken'] = path_user1_rw
json_dict['Permissions'] = "rw"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** list (json->json)")
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Permissions'] == "rw"
# enable map by user
logging.info("\n*** enable map by user1")
json_dict = {}
json_dict['PathOrToken'] = path_user1_rw
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user1:user1pw", json_dict=json_dict)
# check PROPFIND/priviledges item as user
logging.info("\n*** PROPFIND/priviledges item as user")
priviledges_list = self._propfind_priviledges(path_user1_rw, login="user1:user1pw")
assert "D:read" in priviledges_list
assert "D:write-content" in priviledges_list
assert "D:write-properties" not in priviledges_list
assert "D:write" not in priviledges_list
assert "D:all" not in priviledges_list
logging.info("\n*** create map user1/owner1 with adjusted default permissions -> 200")
self.configure({"sharing": {"default_permissions_create_map": "RrWw"}})
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_owner1_RrWw
json_dict['PathOrToken'] = path_user1_RrWw
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
logging.info("\n*** list (json->json)")
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Permissions'] == "RrWw"
# create token
self.configure({"sharing": {"default_permissions_create_token": "r"}})
json_dict = {}
json_dict['PathMapped'] = path_owner1
logging.info("\n*** create token user1/owner1 r -> 200")
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
Token = answer_dict['PathOrToken']
logging.info("\n*** list (json->json)")
json_dict['PathOrToken'] = Token
_, headers, answer = self._sharing_api_json("token", "list", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Permissions'] == "r"
logging.info("\n*** create token user1/owner1 rw -> 200")
json_dict['Permissions'] = "rw"
del json_dict['PathOrToken']
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
Token = answer_dict['PathOrToken']
logging.info("\n*** list (json->json)")
del json_dict['Permissions']
json_dict['PathOrToken'] = Token
_, headers, answer = self._sharing_api_json("token", "list", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Permissions'] == "rw"
logging.info("\n*** create token user1/owner1 with adjusted default permissions -> 200")
self.configure({"sharing": {"default_permissions_create_token": "RrWw"}})
del json_dict['PathOrToken']
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
Token = answer_dict['PathOrToken']
logging.info("\n*** list (json->json)")
json_dict['PathOrToken'] = Token
_, headers, answer = self._sharing_api_json("token", "list", check=200, login="owner1:owner1pw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Permissions'] == "RrWw"
def test_sharing_api_map_report_base(self) -> None:
"""share-by-map API usage tests related to report."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"permit_properties_overlay": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_mapped = "/owner/abook1.vcf/"
path_shared_r = "/user/abook-shared-by-owner.vcf/"
logging.info("\n*** prepare and test access")
self.create_addressbook(path_mapped, login="owner:ownerpw")
contact = get_file_content("contact1.vcf")
path_mapped_item = path_mapped + "contact.vcf"
path_shared_item = path_shared_r + "contact.vcf"
self.put(path_mapped_item, contact, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# create map
logging.info("\n*** create map user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# check REPORT as owner
logging.info("\n*** REPORT collection owner -> ok")
_, responses = self.report(path_mapped, """\
<?xml version="1.0"?>
<CR:addressbook-multiget xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav">
<prop>
<getetag />
<CR:address-data />
</prop>
<href>""" + path_mapped_item + """</href>
</CR:addressbook-multiget>""", login="owner:ownerpw")
assert len(responses) == 1
logging.info("response: %r", responses)
response = responses[path_mapped_item]
assert isinstance(response, dict)
status, prop = response["D:getetag"]
assert status == 200 and prop.text
# check REPORT as user
logging.info("\n*** REPORT collection user -> ok")
_, responses = self.report(path_shared_r, """\
<?xml version="1.0"?>
<CR:addressbook-multiget xmlns="DAV:" xmlns:CR="urn:ietf:params:xml:ns:carddav">
<prop>
<getetag />
<CR:address-data />
</prop>
<href>""" + path_shared_r + """</href>
</CR:addressbook-multiget>""", login="user:userpw")
assert len(responses) == 1
logging.info("response: %r", responses)
response = responses[path_shared_item]
assert isinstance(response, dict)
status, prop = response["D:getetag"]
assert status == 200 and prop.text
def test_sharing_api_map_propfind_overlay_api_base(self) -> None:
"""share-by-map API usage tests related to proppatch."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"permit_properties_overlay": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
form_array: Sequence[str]
json_dict: dict
path_mapped = "/owner/calendarPFO.ics/"
path_shared_r = "/user/calendarPFO-shared-by-owner-r.ics/"
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["D:current-user-principal"]
assert status == 200 and len(prop) == 1
element = prop.find(xmlutils.make_clark("D:href"))
assert element is not None and element.text == "/owner/"
# execute PROPPATCH as owner
logging.info("\n*** PROPPATCH collection owner -> ok")
_, responses = self.proppatch(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<D:propertyupdate xmlns:D="DAV:">
<D:set>
<D:prop>
<I:calendar-color xmlns:I="http://apple.com/ns/ical/">#AAAAAA</I:calendar-color>
<C:calendar-description xmlns:C="urn:ietf:params:xml:ns:caldav">ICAL-OWNER</C:calendar-description>
</D:prop>
</D:set>
</D:propertyupdate>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 2
status, prop = response["ICAL:calendar-color"]
assert status == 200 and not prop.text
status, prop = response["C:calendar-description"]
assert status == 200 and not prop.text
# verify PROPPATCH by owner
logging.info("\n*** PROPFIND collection owner -> ok")
propfind_calendar_color = get_file_content("propfind_multiple.xml")
_, responses = self.propfind(path_mapped, propfind_calendar_color, login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int)
status, prop = response["C:calendar-description"]
logging.debug("calendar-description: %r", prop.text)
assert status == 200 and prop.text == "ICAL-OWNER"
status, prop = response["ICAL:calendar-color"]
logging.debug("calendar-color: %r", prop.text)
assert status == 200 and prop.text == "#AAAAAA"
# create map
logging.info("\n*** create map user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# verify PROPFIND as user
logging.info("\n*** PROPFIND collection owner -> ok")
propfind_calendar_color = get_file_content("propfind_multiple.xml")
_, responses = self.propfind(path_mapped, propfind_calendar_color, login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int)
status, prop = response["C:calendar-description"]
logging.debug("calendar-description: %r", prop.text)
assert status == 200 and prop.text == "ICAL-OWNER"
status, prop = response["ICAL:calendar-color"]
logging.debug("calendar-color: %r", prop.text)
assert status == 200 and prop.text == "#AAAAAA"
# update map by user
logging.info("\n*** update map by user (json)")
json_dict = {}
json_dict['PathOrToken'] = path_shared_r
json_dict['Properties'] = {"C:calendar-description": "ICAL-USER", "ICAL:calendar-color": "#BBBBBB"}
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** list (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
logging.info("\n*** list (json->csv)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/csv")
logging.info("\n*** list (json->txt)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/plain")
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay) -> ok")
propfind_calendar_color = get_file_content("propfind_multiple.xml")
_, responses = self.propfind(path_shared_r, propfind_calendar_color, login="user:userpw")
logging.info("response: %r", responses)
response = responses[path_shared_r]
assert not isinstance(response, int)
status, prop = response["C:calendar-description"]
logging.debug("calendar-description: %r", prop.text)
assert status == 200 and prop.text == "ICAL-USER"
status, prop = response["ICAL:calendar-color"]
logging.debug("calendar-color: %r", prop.text)
assert status == 200 and prop.text == "#BBBBBB"
# verify overlay not visible by owner
logging.info("\n*** PROPFIND collection owner -> ok")
propfind_calendar_color = get_file_content("propfind_multiple.xml")
_, responses = self.propfind(path_mapped, propfind_calendar_color, login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int)
status, prop = response["C:calendar-description"]
logging.debug("calendar-description: %r", prop.text)
assert status == 200 and prop.text == "ICAL-OWNER"
status, prop = response["ICAL:calendar-color"]
logging.debug("calendar-color: %r", prop.text)
assert status == 200 and prop.text == "#AAAAAA"
# update map by user
logging.info("\n*** update map by user (form)")
form_array = []
form_array.append("PathOrToken=" + path_shared_r)
form_array.append("Properties='C:calendar-description'='ICAL-USER-NEW'")
form_array.append("Properties='ICAL:calendar-color'='#CCCCCC'")
_, headers, answer = self._sharing_api_form("map", "update", check=200, login="user:userpw", form_array=form_array)
assert "Status='success'" in answer
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay) -> ok")
propfind_calendar_color = get_file_content("propfind_multiple.xml")
_, responses = self.propfind(path_shared_r, propfind_calendar_color, login="user:userpw")
logging.info("response: %r", responses)
response = responses[path_shared_r]
assert not isinstance(response, int)
status, prop = response["C:calendar-description"]
logging.debug("calendar-description: %r", prop.text)
assert status == 200 and prop.text == "ICAL-USER-NEW"
status, prop = response["ICAL:calendar-color"]
logging.debug("calendar-color: %r", prop.text)
assert status == 200 and prop.text == "#CCCCCC"
# update map by user
logging.info("\n*** update properties with buggyy ones by user (form)")
form_array = ["User=" + "user"]
form_array.append("PathOrToken=" + path_shared_r)
form_array.append("Properties=BUGGYENTRY=BUGGYVALUE")
_, headers, answer = self._sharing_api_form("map", "update", check=400, login="user:userpw", form_array=form_array)
def test_sharing_api_map_propfind_overlay_api_delete(self) -> None:
"""share-by-map API usage tests related to proppatch."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"permit_properties_overlay": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
form_array: Sequence[str]
json_dict: dict
path_mapped = "/owner/calendarPFD.ics/"
path_shared_r = "/user/calendarPFD-shared-by-owner-r.ics/"
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["D:current-user-principal"]
assert status == 200 and len(prop) == 1
element = prop.find(xmlutils.make_clark("D:href"))
assert element is not None and element.text == "/owner/"
# create map
logging.info("\n*** create map user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# set properties by user
logging.info("\n*** set properties by user (form)")
form_array = []
form_array.append("PathOrToken=" + path_shared_r)
form_array.append("Properties='ICAL:calendar-color'='#CCCCCC'")
_, headers, answer = self._sharing_api_form("map", "update", check=200, login="user:userpw", form_array=form_array)
# check that properties are existing in map
logging.info("\n*** list and check for properties (json->json)")
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == '#CCCCCC'
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay) -> ok")
propfind_calendar_color = get_file_content("propfind_calendar_color.xml")
_, responses = self.propfind(path_shared_r, propfind_calendar_color, login="user:userpw")
logging.info("response: %r", responses)
response = responses[path_shared_r]
assert not isinstance(response, int)
status, prop = response["ICAL:calendar-color"]
logging.debug("calendar-color: %r", prop.text)
assert status == 200 and prop.text == "#CCCCCC"
# clear properties by user
logging.info("\n*** clear properties by user (form)")
form_array = []
form_array.append("PathOrToken=" + path_shared_r)
form_array.append("Properties=")
_, headers, answer = self._sharing_api_form("map", "update", check=200, login="user:userpw", form_array=form_array)
# check that properties are cleared
logging.info("\n*** list and check for cleared properties (json->json)")
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="user:userpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Properties'] is not None
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay no longer exists) -> ok")
propfind_calendar_color = get_file_content("propfind_calendar_color.xml")
_, responses = self.propfind(path_shared_r, propfind_calendar_color, login="user:userpw")
logging.info("response: %r", responses)
response = responses[path_shared_r]
assert not isinstance(response, int)
status, prop = response["ICAL:calendar-color"]
logging.debug("calendar-color: %r", prop.text)
assert status == 404
def test_sharing_api_map_propfind_overlay_api_permissions(self) -> None:
"""share-by-map API usage tests related to proppatch."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
path_mapped = "/owner/calendarPFOAP.ics/"
path_shared_r = "/user/calendarPFOAP-shared-by-owner-r.ics/"
logging.info("\n*** prepare and test access")
self.mkcalendar(path_mapped, login="owner:ownerpw")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["D:current-user-principal"]
assert status == 200 and len(prop) == 1
element = prop.find(xmlutils.make_clark("D:href"))
assert element is not None and element.text == "/owner/"
# execute PROPPATCH as owner
logging.info("\n*** PROPPATCH collection owner -> ok")
self._proppatch_calendar_color(path_mapped, login="owner:ownerpw", color="#AAAAAA")
# verify PROPPATCH by owner
logging.info("\n*** PROPFIND collection owner (verify collection change) -> ok")
color = self._propfind_calendar_color(path_mapped, login="owner:ownerpw")
assert color == "#AAAAAA"
# create map
logging.info("\n*** create map user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
logging.info("\n*** list (json->json) db=" + db_type)
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Permissions'] == "r"
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
logging.info("\n*** list after enable (json->json) db=" + db_type)
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Permissions'] == "r"
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay, color back to owner) -> ok")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#AAAAAA"
self.configure({"sharing": {"permit_properties_overlay": False}})
# update map by user
logging.info("\n*** update map by user (json) -> 403 (no overlay permitted)")
json_dict = {}
json_dict['PathOrToken'] = path_shared_r
json_dict['Properties'] = {"ICAL:calendar-color": "#BBBBBB"}
_, headers, answer = self._sharing_api_json("map", "update", check=403, login="user:userpw", json_dict=json_dict)
self.configure({"sharing": {"permit_properties_overlay": True}})
logging.info("\n*** update map by user (json) -> 200 (overlay permitted)")
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay) -> ok")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#BBBBBB"
# one property have to be visible
logging.info("\n*** list check for one property (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == "#BBBBBB"
# update map by owner
logging.info("\n*** update map by owner (disable property overlay)")
json_dict = {}
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "rp"
json_dict['User'] = "user"
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** list after update by owner (json->json) db=" + db_type)
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert answer_dict['Content'][0]['Permissions'] == "rp"
logging.info("\n*** update map by user (json) -> 403 (no overlay permitted by share permissions)")
json_dict = {}
json_dict['PathOrToken'] = path_shared_r
json_dict['Properties'] = {"ICAL:calendar-color": "#CCCCCC"}
_, headers, answer = self._sharing_api_json("map", "update", check=403, login="user:userpw", json_dict=json_dict)
# one property have to be visible
logging.info("\n*** list check for one property (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == "#BBBBBB"
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay) -> ok")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#BBBBBB"
# update map by owner
logging.info("\n*** update map by owner (disable property overlay)")
json_dict = {}
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "rP"
json_dict['User'] = "user"
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
# one property have to be visible
logging.info("\n*** list check for one property (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == "#BBBBBB"
logging.info("\n*** update map by user (json) -> 200 (overlay permitted by share permissions)")
json_dict = {}
json_dict['PathOrToken'] = path_shared_r
json_dict['Properties'] = {"ICAL:calendar-color": "#CCCCCC"}
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay) -> ok")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#CCCCCC"
self.configure({"sharing": {"permit_properties_overlay": True}})
# update map by owner
logging.info("\n*** update map by owner (disable property overlay)")
json_dict = {}
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "rp"
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
# one property have to be visible
logging.info("\n*** list check for one property (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == "#CCCCCC"
logging.info("\n*** update map by user (json) -> 403 (overlay permitted but denied by share permissions)")
json_dict = {}
json_dict['PathOrToken'] = path_shared_r
json_dict['Properties'] = {"ICAL:calendar-color": "#EEEEEE"}
_, headers, answer = self._sharing_api_json("map", "update", check=403, login="user:userpw", json_dict=json_dict)
def test_sharing_api_map_propfind_overlay_proppatch(self) -> None:
"""share-by-map API usage tests related to proppatch."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": "True",
"permit_create_token": "True",
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
logging.info("\n*** prepare and test access")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
path_mapped = "/owner/calendarPFP-" + db_type + ".ics/"
path_shared_r = "/user/calendarPFP-shared-by-owner-r-" + db_type + ".ics/"
self.mkcalendar(path_mapped, login="owner:ownerpw")
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["D:current-user-principal"]
assert status == 200 and len(prop) == 1
element = prop.find(xmlutils.make_clark("D:href"))
assert element is not None and element.text == "/owner/"
# execute PROPPATCH as owner
logging.info("\n*** PROPPATCH collection owner -> ok")
self._proppatch_calendar_color(path_mapped, login="owner:ownerpw", color="#AAAAAA")
# verify PROPPATCH by owner
logging.info("\n*** PROPFIND collection owner (verify collection change) -> ok")
color = self._propfind_calendar_color(path_mapped, login="owner:ownerpw")
assert color == "#AAAAAA"
# create map
logging.info("\n*** create map user/owner:rP -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "rP"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# check PROPFIND/priviledges item as user
logging.info("\n*** PROPFIND/priviledges item as user -> calendar")
priviledges_list = self._propfind_priviledges(path_shared_r, login="user:userpw")
assert "D:read" in priviledges_list
assert "D:write-content" not in priviledges_list
assert "D:write-properties" in priviledges_list
assert "D:write" not in priviledges_list
assert "D:all" not in priviledges_list
# verify PROPPATCH as user
logging.info("\n*** PROPFIND collection user -> color #AAAAAA")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#AAAAAA"
# execute PROPPATCH as user
logging.info("\n*** PROPPATCH collection user -> ok")
self._proppatch_calendar_color(path_shared_r, login="user:userpw", color="#BBBBBB")
logging.info("\n*** list (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
logging.info("\n*** list (json->csv)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/csv")
logging.info("\n*** list (json->txt)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/plain")
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay) -> ok")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#BBBBBB"
# verify overlay not visible by owner
logging.info("\n*** PROPFIND collection owner (no collection change) -> ok")
color = self._propfind_calendar_color(path_mapped, login="owner:ownerpw")
assert color == "#AAAAAA"
# execute PROPPATCH as user (delete color)
logging.info("\n*** PROPPATCH collection user (delete color) -> ok")
self._proppatch_calendar_color_remove(path_shared_r, login="user:userpw")
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay, color back to owner) -> ok")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#AAAAAA"
# update map by owner
logging.info("\n*** update map by owner (disable property overlay)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "rwe"
json_dict['User'] = "user"
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
# execute PROPPATCH as user
logging.info("\n*** PROPPATCH collection user (set color/collection) -> ok")
self._proppatch_calendar_color(path_shared_r, login="user:userpw", color="#DDDDDD")
# verify overlay as user
logging.info("\n*** PROPFIND collection user (collection color changed) -> ok")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#DDDDDD"
# verify overlay visible by owner
logging.info("\n*** PROPFIND collection owner (visible change by user) -> ok")
color = self._propfind_calendar_color(path_mapped, login="owner:ownerpw")
assert color == "#DDDDDD"
# update map by owner
logging.info("\n*** update map by owner (enable property overlay)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "rwE"
json_dict['User'] = "user"
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
# execute PROPPATCH as user
logging.info("\n*** PROPPATCH collection user (set color rw, enforce overlay enabled by default) -> ok")
self._proppatch_calendar_color(path_shared_r, login="user:userpw", color="#EEEEEE")
# verify overlay visible by owner
logging.info("\n*** PROPFIND collection owner (invisible change by user) -> ok")
color = self._propfind_calendar_color(path_mapped, login="owner:ownerpw")
assert color == "#DDDDDD"
# verify overlay as user
logging.info("\n*** PROPFIND collection user (overlay: color) -> ok")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#EEEEEE"
# update map by owner
logging.info("\n*** update map by owner (enable property overlay)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "rwe"
json_dict['User'] = "user"
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
# execute PROPPATCH as user
logging.info("\n*** PROPPATCH collection user (set color rwe, enforce overlay enabled by default) -> ok")
self._proppatch_calendar_color(path_shared_r, login="user:userpw", color="#EEEE00")
# verify overlay visible by owner
logging.info("\n*** PROPFIND collection owner (visible change) -> ok")
color = self._propfind_calendar_color(path_mapped, login="owner:ownerpw")
assert color == "#EEEE00"
self.configure({"sharing": {"enforce_properties_overlay": False}})
# update map by owner
logging.info("\n*** update map by owner (enable property overlay)")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "rw"
json_dict['User'] = "user"
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
logging.info("\n*** PROPPATCH collection user (set color rwe but enforce disabled) -> ok")
self._proppatch_calendar_color(path_shared_r, login="user:userpw", color="#FFFFFF")
# verify visible by owner
logging.info("\n*** PROPFIND collection owner (visible change) -> ok")
color = self._propfind_calendar_color(path_mapped, login="owner:ownerpw")
assert color == "#FFFFFF"
def test_sharing_api_map_propfind_overlay_partial(self) -> None:
"""share-by-map API usage tests related to partial overlay."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_map": True,
"permit_create_token": True,
"permit_properties_overlay": "True",
"enforce_properties_overlay": "True",
"collection_by_map": "True",
"collection_by_token": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
logging.info("\n*** prepare and test access")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
path_mapped = "/owner/calendarPFP-" + db_type + ".ics/"
path_shared_r = "/user/calendarPFP-shared-by-owner-r-" + db_type + ".ics/"
self.mkcalendar(path_mapped, login="owner:ownerpw")
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<prop>
<current-user-principal />
</prop>
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int) and len(response) == 1
status, prop = response["D:current-user-principal"]
assert status == 200 and len(prop) == 1
element = prop.find(xmlutils.make_clark("D:href"))
assert element is not None and element.text == "/owner/"
# execute PROPPATCH color as owner
logging.info("\n*** PROPPATCH color collection owner -> ok")
self._proppatch_calendar_color(path_mapped, login="owner:ownerpw", color="#AAAAAA")
# verify PROPPATCH color by owner
logging.info("\n*** PROPFIND color collection owner (verify collection change) -> ok")
color = self._propfind_calendar_color(path_mapped, login="owner:ownerpw")
assert color == "#AAAAAA"
# execute PROPPATCH description as owner
logging.info("\n*** PROPPATCH description collection owner -> ok")
self._proppatch_calendar_description(path_mapped, login="owner:ownerpw", description="OWNER")
# verify PROPPATCH description by owner
logging.info("\n*** PROPFIND description collection owner (verify collection change) -> ok")
description = self._propfind_calendar_description(path_mapped, login="owner:ownerpw")
assert description == "OWNER"
# create map
logging.info("\n*** create map user/owner:rP -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "rP"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# verify PROPPATCH as user
logging.info("\n*** PROPFIND color collection user -> #AAAAAA")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#AAAAAA"
logging.info("\n*** PROPFIND description collection collection user -> ok")
description = self._propfind_calendar_description(path_shared_r, login="user:userpw")
assert description == "OWNER"
# execute PROPPATCH color as user
logging.info("\n*** PROPPATCH color collection user -> ok")
self._proppatch_calendar_color(path_shared_r, login="user:userpw", color="#BBBBBB")
# one property has to be visible
logging.info("\n*** list (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == "#BBBBBB"
# execute PROPPATCH description as user
logging.info("\n*** PROPPATCH description collection user -> ok")
self._proppatch_calendar_description(path_shared_r, login="user:userpw", description="USER")
# both properties have to be visible
logging.info("\n*** list check for both properties (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == "#BBBBBB"
assert 'C:calendar-description' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['C:calendar-description'] == "USER"
# verify PROPPATCH as user
logging.info("\n*** PROPFIND color collection user -> #BBBBBB")
color = self._propfind_calendar_color(path_shared_r, login="user:userpw")
assert color == "#BBBBBB"
logging.info("\n*** PROPFIND description collection collection user -> ok")
description = self._propfind_calendar_description(path_shared_r, login="user:userpw")
assert description == "USER"
# execute PROPPATCH DELETE description as user
logging.info("\n*** PROPPATCH DELETE description collection user -> ok")
self._proppatch_calendar_description_remove(path_shared_r, login="user:userpw")
# one property has to survive
logging.info("\n*** list check for still one property (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == "#BBBBBB"
# set properties by user using API
logging.info("\n*** set properties by user color overwrite (form)")
form_array = []
form_array.append("PathOrToken=" + path_shared_r)
form_array.append("Properties='ICAL:calendar-color'='#CCCCCC'")
_, headers, answer = self._sharing_api_form("map", "update", check=200, login="user:userpw", form_array=form_array)
# one property has to survive
logging.info("\n*** list check for still one property (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == "#CCCCCC"
assert 'C:calendar-description' not in answer_dict['Content'][0]['Properties']
# set property by user using API
logging.info("\n*** set properties by user description extension (form)")
form_array = []
form_array.append("PathOrToken=" + path_shared_r)
form_array.append("Properties='C:calendar-description'='USER-OWNER'")
_, headers, answer = self._sharing_api_form("map", "update", check=200, login="user:userpw", form_array=form_array)
# both properties have to be visible
logging.info("\n*** list check for both properties (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['ICAL:calendar-color'] == "#CCCCCC"
assert 'C:calendar-description' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['C:calendar-description'] == "USER-OWNER"
# delete property by user using API
logging.info("\n*** delete property by user color (form)")
form_array = []
form_array.append("PathOrToken=" + path_shared_r)
form_array.append("Properties='ICAL:calendar-color'=''")
_, headers, answer = self._sharing_api_form("map", "update", check=200, login="user:userpw", form_array=form_array)
# only one property has to be visible
logging.info("\n*** list check for single properties (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' not in answer_dict['Content'][0]['Properties']
assert 'C:calendar-description' in answer_dict['Content'][0]['Properties']
assert answer_dict['Content'][0]['Properties']['C:calendar-description'] == "USER-OWNER"
# clear all propertie by user using API
logging.info("\n*** delete property by user color (form)")
form_array = []
form_array.append("PathOrToken=" + path_shared_r)
form_array.append("Properties=")
_, headers, answer = self._sharing_api_form("map", "update", check=200, login="user:userpw", form_array=form_array)
# no property has to be visible
logging.info("\n*** list check empty properties (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' not in answer_dict['Content'][0]['Properties']
assert 'C:calendar-description' not in answer_dict['Content'][0]['Properties']
# set properties by user using API
logging.info("\n*** set properties by user (json)")
json_dict = {}
json_dict["PathOrToken"] = path_shared_r
json_dict["Properties"] = {'C:calendar-description': 'USER-OWNER', 'ICAL:calendar-color': '#DDDDDD'}
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
# both properties have to be visible
logging.info("\n*** list check for both properties (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert 'C:calendar-description' in answer_dict['Content'][0]['Properties']
# delete on property by user using API
logging.info("\n*** delete property by user color (json)")
json_dict = {}
json_dict["PathOrToken"] = path_shared_r
json_dict["Properties"] = {'C:calendar-description': ''}
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
# one property have to be visible
logging.info("\n*** list check for one property (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' in answer_dict['Content'][0]['Properties']
assert 'C:calendar-description' not in answer_dict['Content'][0]['Properties']
# delete all propertie by user using API
logging.info("\n*** delete all properties by user (json)")
json_dict = {}
json_dict["PathOrToken"] = path_shared_r
json_dict["Properties"] = {}
_, headers, answer = self._sharing_api_json("map", "update", check=200, login="user:userpw", json_dict=json_dict)
# no property has to be visible
logging.info("\n*** list check empty properties (json->json)")
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("map", "list", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
assert answer_dict['Lines'] == 1
assert 'ICAL:calendar-color' not in answer_dict['Content'][0]['Properties']
assert 'C:calendar-description' not in answer_dict['Content'][0]['Properties']
def test_sharing_api_bday_basic(self) -> None:
"""share-by-bday basic tests."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_bday": True,
"permit_properties_overlay": "True",
"enforce_properties_overlay": "True",
"collection_by_bday": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"response_header_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
logging.info("\n*** prepare and test access")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
path_mapped = "/owner/adressbook-" + db_type + ".vcf/"
path_shared_r = "/user/calendar-bday-abook-shared-by-owner-r-" + db_type + ".ics/"
self.create_addressbook(path_mapped, login="owner:ownerpw")
contact = get_file_content("contact1.vcf")
path = path_mapped + "/contact1.vcf"
self.put(path, contact, login="owner:ownerpw")
contact = get_file_content("contact2-with-bday.vcf")
path = path_mapped + "/contact2-with-bday.vcf"
self.put(path, contact, login="owner:ownerpw")
contact = get_file_content("contact3-with-bday.vcf")
path = path_mapped + "/contact3-with-bday.vcf"
self.put(path, contact, login="owner:ownerpw")
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<propname />
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int)
assert "CR:supported-address-data" in response
# execute GET as owner
logging.info("\n*** GET VCF collection owner -> ok")
_, headers, answer = self.request("GET", path_mapped, login="owner:ownerpw")
assert "contact1" in answer
assert "contact2" in answer
# title from fallback
assert 'Content-Disposition' in headers
assert 'Address%20book.vcf' in headers['Content-Disposition']
# create map
logging.info("\n*** create bday user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
json_dict['Properties'] = {"D:displayname": "Test-BDAY"}
_, headers, answer = self._sharing_api_json("bday", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# enable map by user
logging.info("\n*** enable bday by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_r
_, headers, answer = self._sharing_api_json("bday", "enable", check=200, login="user:userpw", json_dict=json_dict)
# check PROPFIND item as user
logging.info("\n*** PROPFIND item as user -> calendar")
response = self._propfind_allprop(path_shared_r, login="user:userpw")
logging.debug("response: %r", response)
assert "CR:supported-address-data" not in response
assert "D:sync-token" not in response
assert "C:supported-calendar-component-set" in response
assert "D:current-user-privilege-set" in response
# check PROPFIND/priviledges item as user
logging.info("\n*** PROPFIND/priviledges item as user -> calendar")
priviledges_list = self._propfind_priviledges(path_shared_r, login="user:userpw")
assert "D:read" in priviledges_list
assert "D:write-content" not in priviledges_list
assert "D:write-properties" in priviledges_list
assert "D:write" not in priviledges_list
assert "D:all" not in priviledges_list
# verify content as user
logging.info("\n*** GET collection user -> ok")
_, headers, answer = self.request("GET", path_shared_r, login="user:userpw")
assert "BEGIN:VCARD" not in answer
assert "BEGIN:VCALENDAR" in answer
assert "RRULE:FREQ=YEARLY" in answer
assert "DTSTART;VALUE=DATE:19700101" in answer
assert "DTEND;VALUE=DATE:19700102" in answer
assert "TRANSP:TRANSPARENT" in answer
assert "DESCRIPTION:BDAY=1970-01-01" in answer
# content type must be adjusted
assert 'Content-Type' in headers
assert 'text/calendar' in headers['Content-Type']
# title from Properties
assert 'Content-Disposition' in headers
assert 'Test-BDAY.ics' in headers['Content-Disposition']
# verify report as user
logging.info("\n*** REPORT collection user -> ok")
_, responses = self.report(path_shared_r, """\
<?xml version="1.0" encoding="utf-8" ?>
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
<D:prop xmlns:D="DAV:">
<D:getetag/>
</D:prop>
</C:calendar-query>""", login="user:userpw")
logging.debug("resonses: %r", responses)
assert path_shared_r + "contact2-with-bday.ics" in responses
assert path_shared_r + "contact3-with-bday.ics" in responses
assert path_shared_r + "contact1.ics" not in responses
# get elements as user
logging.info("\n*** REPORT collection entries user -> ok")
_, responses = self.report(path_shared_r, """\
<?xml version="1.0"?>
<C:calendar-multiget xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<prop>
<getetag />
<C:calendar-data />
</prop>
<href>""" + path_shared_r + "contact2-with-bday.ics" + """</href>
<href>""" + path_shared_r + "contact3-with-bday.ics" + """</href>
</C:calendar-multiget>""", login="user:userpw")
logging.debug("resonses: %r", responses)
assert path_shared_r + "contact2-with-bday.ics" in responses
assert path_shared_r + "contact3-with-bday.ics" in responses
assert path_shared_r + "contact1.ics" not in responses
# timerange filter elements as user
logging.info("\n*** REPORT collection entries with timerange user -> ok")
_, responses = self.report(path_shared_r, """\
<?xml version="1.0"?>
<C:calendar-query xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav">
<prop>
<getcontenttype />
<getetag />
<C:calendar-data />
</prop>
<C:filter>
<C:comp-filter name="VCALENDAR">
<C:comp-filter name="VEVENT">
<C:time-range start="20251124T000000Z" end="20260715T000000Z" />
</C:comp-filter>
</C:comp-filter>
</C:filter>
</C:calendar-query>""", login="user:userpw")
logging.debug("resonses: %r", responses)
assert path_shared_r + "contact2-with-bday.ics" in responses
assert path_shared_r + "contact3-with-bday.ics" in responses
assert path_shared_r + "contact1.ics" not in responses
logging.info("\n*** PROPFIND collection entries user -> ok")
_, responses = self.propfind(path_shared_r, """\
<?xml version="1.0"?>
<propfind xmlns="DAV:">
<prop>
<getcontenttype />
<resourcetype />
<getetag />
</prop>
</propfind>""", login="user:userpw", HTTP_DEPTH="1")
logging.debug("resonses: %r", responses)
assert path_shared_r + "contact2-with-bday.ics" in responses
assert path_shared_r + "contact3-with-bday.ics" in responses
assert path_shared_r + "contact1.ics" not in responses
response = responses[path_shared_r + "contact2-with-bday.ics"]
assert not isinstance(response, int)
status, prop = response["D:getcontenttype"]
assert "text/calendar" in str(prop.text)
def test_sharing_api_bday_complex(self) -> None:
"""share-by-bday complex tests."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_bday": True,
"permit_create_map": True,
"permit_properties_overlay": "True",
"enforce_properties_overlay": "True",
"collection_by_map": "True",
"collection_by_bday": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
logging.info("\n*** prepare and test access")
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
path_mapped = "/owner/adressbook-" + db_type + ".vcf/"
path_user = "/user/"
path_shared_bday = path_user + "calendar-bday-abook-shared-by-owner-r-" + db_type + ".ics/"
path_shared_map = path_user + "abook-shared-by-owner-r-" + db_type + ".vcf/"
self.create_addressbook(path_mapped, login="owner:ownerpw")
contact = get_file_content("contact1.vcf")
path = path_mapped + "/contact1.vcf"
self.put(path, contact, login="owner:ownerpw")
contact = get_file_content("contact2-with-bday.vcf")
path = path_mapped + "/contact2-with-bday.vcf"
self.put(path, contact, login="owner:ownerpw")
contact = get_file_content("contact3-with-bday.vcf")
path = path_mapped + "/contact3-with-bday.vcf"
self.put(path, contact, login="owner:ownerpw")
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<propname />
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int)
assert "CR:supported-address-data" in response
# execute GET as owner
logging.info("\n*** GET VCF collection owner -> ok")
_, answer = self.get(path_mapped, login="owner:ownerpw")
assert "contact1" in answer
assert "contact2" in answer
# create bday
logging.info("\n*** create bday user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_bday
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("bday", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# enable bday by user
logging.info("\n*** enable bday by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_bday
_, headers, answer = self._sharing_api_json("bday", "enable", check=200, login="user:userpw", json_dict=json_dict)
# unhide bday by user
logging.info("\n*** unhide bday by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_bday
_, headers, answer = self._sharing_api_json("bday", "unhide", check=200, login="user:userpw", json_dict=json_dict)
# create map
logging.info("\n*** create map user/owner:r -> ok")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_map
json_dict['Permissions'] = "r"
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("map", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
answer_dict = json.loads(answer)
assert answer_dict['Status'] == "success"
# enable map by user
logging.info("\n*** enable map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_map
_, headers, answer = self._sharing_api_json("map", "enable", check=200, login="user:userpw", json_dict=json_dict)
# unhide map by user
logging.info("\n*** unhide map by user")
json_dict = {}
json_dict['User'] = "user"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared_map
_, headers, answer = self._sharing_api_json("map", "unhide", check=200, login="user:userpw", json_dict=json_dict)
# check PROPFIND item as user
logging.info("\n*** PROPFIND all as user")
_, responses = self.propfind(path_user, """\
<?xml version="1.0"?>
<propfind xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:CR="urn:ietf:params:xml:ns:carddav" xmlns:CS="http://calendarserver.org/ns/" xmlns:ICAL="http://apple.com/ns/ical/" xmlns:RADICALE="http://radicale.org/ns/" xmlns:ns3="http://inf-it.com/ns/ab/">
<prop>
<resourcetype />
<RADICALE:displayname />
<ICAL:calendar-color />
<ns3:addressbook-color />
<C:calendar-description />
<C:supported-calendar-component-set />
<CR:addressbook-description />
<CS:source />
<RADICALE:getcontentcount />
<getcontentlength />
</prop>
</propfind>""", login="user:userpw", HTTP_DEPTH="1")
# logging.debug("responses: %r", responses)
response = responses[path_shared_map]
assert not isinstance(response, int)
logging.debug("response %r: %r", path_shared_map, response)
assert "C:supported-calendar-component-set" in response
response = responses[path_shared_bday]
assert not isinstance(response, int)
logging.debug("response %r: %r", path_shared_bday, response)
assert "C:supported-calendar-component-set" in response
# check PROPFIND item as user
logging.info("\n*** PROPFIND all as user (reduced)")
_, responses = self.propfind(path_user, """\
<?xml version="1.0"?>
<propfind xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:CR="urn:ietf:params:xml:ns:carddav" xmlns:CS="http://calendarserver.org/ns/" xmlns:ICAL="http://apple.com/ns/ical/" xmlns:RADICALE="http://radicale.org/ns/" xmlns:ns3="http://inf-it.com/ns/ab/">
<prop>
<resourcetype />
<C:calendar-description />
<CR:addressbook-description />
<RADICALE:getcontentcount />
<getcontentlength />
</prop>
</propfind>""", login="user:userpw", HTTP_DEPTH="1")
# logging.debug("responses: %r", responses)
response = responses[path_shared_bday]
assert not isinstance(response, int)
logging.debug("response %r: %r", path_shared_bday, response)
assert "D:getcontentlength" in response
assert "RADICALE:getcontentcount" in response
status, prop = response["D:getcontentlength"]
assert int(str(prop.text)) > 600
status, prop = response["RADICALE:getcontentcount"]
assert int(str(prop.text)) == 2
def test_sharing_api_bday_self(self) -> None:
"""share-by-bday to self tests."""
self.configure({"auth": {"type": "htpasswd",
"htpasswd_filename": self.htpasswd_file_path,
"htpasswd_encryption": "plain"},
"sharing": {
"type": "csv",
"permit_create_bday": True,
"permit_properties_overlay": "True",
"enforce_properties_overlay": "True",
"collection_by_bday": "True"},
"logging": {"request_header_on_debug": "False",
"response_content_on_debug": "True",
"request_content_on_debug": "True"},
"rights": {"type": "owner_only"}})
json_dict: dict
logging.info("\n*** prepare and test access")
path_owner = "/owner/"
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
path_mapped = path_owner + "abook-" + db_type + ".vcf/"
path_shared = path_owner + "cal-bday-abook-" + db_type + ".ics/"
self.create_addressbook(path_mapped, login="owner:ownerpw")
contact = get_file_content("contact1.vcf")
path = path_mapped + "/contact1.vcf"
self.put(path, contact, login="owner:ownerpw")
contact = get_file_content("contact2-with-bday.vcf")
path = path_mapped + "/contact2-with-bday.vcf"
self.put(path, contact, login="owner:ownerpw")
contact = get_file_content("contact3-with-bday.vcf")
path = path_mapped + "/contact3-with-bday.vcf"
self.put(path, contact, login="owner:ownerpw")
# check PROPFIND as owner
logging.info("\n*** PROPFIND collection owner -> ok")
_, responses = self.propfind(path_mapped, """\
<?xml version="1.0" encoding="utf-8"?>
<propfind xmlns="DAV:">
<propname />
</propfind>""", login="owner:ownerpw")
logging.info("response: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int)
assert "CR:supported-address-data" in response
# execute GET as owner
logging.info("\n*** GET VCF collection owner -> ok")
_, answer = self.get(path_mapped, login="owner:ownerpw")
assert "contact1" in answer
assert "contact2" in answer
assert "NICKNAME-C3" in answer
# create map
logging.info("\n*** create bday owner to itself -> ok")
json_dict = {}
json_dict['User'] = "owner"
json_dict['PathMapped'] = path_mapped
json_dict['PathOrToken'] = path_shared
json_dict['Enabled'] = False
json_dict['Hidden'] = True
_, headers, answer = self._sharing_api_json("bday", "create", check=200, login="owner:ownerpw", json_dict=json_dict)
# check PROPFIND item as owner
logging.info("\n*** PROPFIND all as owner")
_, responses = self.propfind(path_owner, """\
<?xml version="1.0"?>
<propfind xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:CR="urn:ietf:params:xml:ns:carddav" xmlns:CS="http://calendarserver.org/ns/" xmlns:ICAL="http://apple.com/ns/ical/" xmlns:RADICALE="http://radicale.org/ns/" xmlns:ns3="http://inf-it.com/ns/ab/">
<prop>
<resourcetype />
<RADICALE:displayname />
<ICAL:calendar-color />
<ns3:addressbook-color />
<C:calendar-description />
<C:supported-calendar-component-set />
<CR:addressbook-description />
<CS:source />
<RADICALE:getcontentcount />
<getcontentlength />
</prop>
</propfind>""", login="owner:ownerpw", HTTP_DEPTH="1")
# logging.debug("responses: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int)
logging.debug("response %r: %r", path_mapped, response)
assert "C:supported-calendar-component-set" in response
assert path_shared not in responses
# enable + unhide
logging.info("\n*** enable+unhide bday owner to itself -> ok")
json_dict = {}
json_dict['PathOrToken'] = path_shared
json_dict['Enabled'] = True
json_dict['Hidden'] = False
_, headers, answer = self._sharing_api_json("bday", "update", check=200, login="owner:ownerpw", json_dict=json_dict)
# check PROPFIND item as owner
logging.info("\n*** PROPFIND all as owner")
_, responses = self.propfind(path_owner, """\
<?xml version="1.0"?>
<propfind xmlns="DAV:" xmlns:C="urn:ietf:params:xml:ns:caldav" xmlns:CR="urn:ietf:params:xml:ns:carddav" xmlns:CS="http://calendarserver.org/ns/" xmlns:ICAL="http://apple.com/ns/ical/" xmlns:RADICALE="http://radicale.org/ns/" xmlns:ns3="http://inf-it.com/ns/ab/">
<prop>
<resourcetype />
<RADICALE:displayname />
<ICAL:calendar-color />
<ns3:addressbook-color />
<C:calendar-description />
<C:supported-calendar-component-set />
<CR:addressbook-description />
<CS:source />
<RADICALE:getcontentcount />
<getcontentlength />
</prop>
</propfind>""", login="owner:ownerpw", HTTP_DEPTH="1")
# logging.debug("responses: %r", responses)
response = responses[path_mapped]
assert not isinstance(response, int)
logging.debug("response %r: %r", path_mapped, response)
assert "C:supported-calendar-component-set" in response
assert path_shared in responses
# check PROPFIND item as owner
logging.info("\n*** PROPFIND item as owner -> calendar")
response = self._propfind_allprop(path_shared, login="owner:ownerpw")
logging.debug("response: %r", response)
assert "CR:supported-address-data" not in response
assert "D:sync-token" not in response
assert "C:supported-calendar-component-set" in response
assert "D:current-user-privilege-set" in response