Merge pull request #2000 from pbiering/feature-sharing-permissions

Feature sharing global and granular permissions
This commit is contained in:
Peter Bieringer
2026-02-26 08:22:34 +01:00
committed by GitHub
6 changed files with 494 additions and 10 deletions

View File

@@ -2102,6 +2102,40 @@ Share collection by map
Default: `false`
##### permit_create_token
_(>= 3.7.0)_
Permit create of token-based sharing
Default: `false`
* If `False` it can be explicitly granted by `permissions: t`
* If `True` it can be explicitly forbidden by `permissions: T`
##### permit_create_map
_(>= 3.7.0)_
Permit create of map-based sharing
Default: `false`
* If `False` it can be explicitly granted by `permissions: m`
* If `True` it can be explicitly forbidden by `permissions: M`
##### default_permissions_create_token
Default permissions for create token-based sharing
Default: `r`
##### default_permissions_create_map
Default permissions for map-based sharing
Default: `r`
## Supported Clients
Radicale has been tested with:
@@ -2326,6 +2360,10 @@ The following `permissions` are recognized:
* **d:** deny deleting a collection in case `permit_delete_collection=True` _(>= 3.3.0)_
* **O:** allow overwriting a collection in case `permit_overwrite_collection=False` _(>= 3.3.0)_
* **o:** deny overwriting a collection in case `permit_overwrite_collection=True` _(>= 3.3.0)_
* **T:** permit create of token-based sharing of collection in case `permit_create_token=False` _(>= 3.7.0)_
* **t:** deny create of token-based sharing of collection in case `permit_create_token=True` _(>= 3.7.0)_
* **M:** permit create of map-based sharing of collection in case `permit_create_map= False` _(>= 3.7.0)_
* **m:** deny create of map-based sharing of collection in case `permit_create_map=True` _(>= 3.7.0)_
### Storage

20
config
View File

@@ -311,11 +311,27 @@
# Sharing database path for type 'files'
#database_path = (filesystem_folder)/collection-db/files
# Share collection by token
#collection_by_token = false
# Share collection by map
#collection_by_map = false
# Share collection by token
#collection_by_token = false
# Permit create of token-based sharing
# If False it can be explicitly granted by permissions: t
# If True it can be explicitly forbidden by permissions: T
#permit_create_token = false
# Permit create of map-based sharing
# If False it can be explicitly granted by permissions: m
# If True it can be explicitly forbidden by permissions: M
#permit_create_map = false
# Default permissions for token-based sharing
#default_permissions_create_token = r
# Default permissions for map-based sharing
#default_permissions_create_map = r
[web]

View File

@@ -66,6 +66,13 @@ def positive_float(value: Any) -> float:
return value
def rights_permission(value: Any) -> str:
for permission in value:
if permission not in rights.INTERNAL_PERMISSIONS:
raise ValueError("unsupported permssion %r found in: %r" % (permission, value))
return value
def logging_level(value: Any) -> str:
if value not in ("debug", "info", "warning", "error", "critical"):
raise ValueError("unsupported level: %r" % value)
@@ -468,14 +475,30 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
"value": "",
"help": "database path",
"type": filepath}),
("collection_by_token", {
"value": "false",
"help": "enable sharing of collection by token",
"type": bool}),
("collection_by_map", {
"value": "false",
"help": "enable sharing of collection by map",
"type": bool}),
("collection_by_token", {
("permit_create_token", {
"value": "false",
"help": "enable sharing of collection by token",
"type": bool})])),
"help": "permit create of token-based sharing",
"type": bool}),
("permit_create_map", {
"value": "false",
"help": "permit create of map-based sharing",
"type": bool}),
("default_permissions_create_token", {
"value": "r",
"help": "default permissions for token-based sharing",
"type": rights_permission}),
("default_permissions_create_map", {
"value": "r",
"help": "default permissions for map-based sharing",
"type": rights_permission})])),
("hook", OrderedDict([
("type", {
"value": "none",

View File

@@ -27,6 +27,14 @@ Permissions:
(CalDAV/CardDAV is susceptible to expensive search requests)
- W: write collections (excluding address books and calendars)
- w: write address book and calendar collections
- D: allow deleting a collection in case permit_delete_collection=False (>= 3.3.0)
- d: deny deleting a collection in case permit_delete_collection=True (>= 3.3.0)
- O: allow overwriting a collection in case permit_overwrite_collection=False (>= 3.3.0)
- o: deny overwriting a collection in case permit_overwrite_collection=True (>= 3.3.0)
- T: permit create of token-based sharing of collection in case permit_create_token=False (>= 3.7.0)
- t: deny create of token-based sharing of collection in case permit_create_token=True (>= 3.7.0)
- M: permit create of map-based sharing of collection in case permit_create_map= False (>= 3.7.0)
- m: deny create of map-based sharing of collection in case permit_create_map=True (>= 3.7.0)
Take a look at the class ``BaseRights`` if you want to implement your own.
@@ -39,6 +47,8 @@ from radicale import config, utils
INTERNAL_TYPES: Sequence[str] = ("authenticated", "owner_write", "owner_only",
"from_file")
INTERNAL_PERMISSIONS: str = "RriWwDdOoTtMm"
def load(configuration: "config.Configuration") -> "BaseRights":
"""Load the rights module chosen in configuration."""

View File

@@ -102,8 +102,16 @@ class BaseSharing:
# Sharing
self.sharing_collection_by_map = configuration.get("sharing", "collection_by_map")
self.sharing_collection_by_token = configuration.get("sharing", "collection_by_token")
self.permit_create_token = configuration.get("sharing", "permit_create_token")
self.permit_create_map = configuration.get("sharing", "permit_create_map")
self.default_permissions_create_token = configuration.get("sharing", "default_permissions_create_token")
self.default_permissions_create_map = configuration.get("sharing", "default_permissions_create_map")
logger.info("sharing.collection_by_map : %s", self.sharing_collection_by_map)
logger.info("sharing.collection_by_token: %s", self.sharing_collection_by_token)
logger.info("sharing.permit_create_token: %s", self.permit_create_token)
logger.info("sharing.permit_create_map : %s", self.permit_create_map)
logger.info("sharing.default_permissions_create_token: %r", self.default_permissions_create_token)
logger.info("sharing.default_permissions_create_map : %r", self.default_permissions_create_map)
if ((self.sharing_collection_by_map is False) and (self.sharing_collection_by_token is False)):
logger.info("sharing disabled as no feature is enabled")
@@ -634,7 +642,13 @@ class BaseSharing:
if logger.isEnabledFor(logging.DEBUG):
logger.debug("TRACE/" + api_info + ": start")
if 'Permissions' not in request_data:
Permissions = "r"
if ShareType == "token":
Permissions = self.default_permissions_create_token
elif ShareType == "map":
Permissions = self.default_permissions_create_map
else:
# default
Permissions = "r"
if 'Enabled' in request_data:
EnabledByOwner = config._convert_to_bool(request_data['Enabled'])
@@ -652,10 +666,19 @@ class BaseSharing:
if ShareType == "token":
# check access Permissions
access = Access(self._rights, user, str(PathMapped)) # PathMapped is mandatory
if not access.check("r") and "i" not in access.permissions:
if not access.check("r"):
logger.info("Add sharing-by-token: access to %r not allowed for user %r", PathMapped, user)
return httputils.NOT_ALLOWED
if self.permit_create_token is False:
if "t" not in access.permissions:
logger.info("Add sharing-by-token: access to %r not allowed for user %r (permit=False but explict grant misses 't')", PathMapped, user)
return httputils.NOT_ALLOWED
else:
if "T" in access.permissions:
logger.info("Add sharing-by-token: access to %r not allowed for user %r (permit=True but denied by 'T')", PathMapped, user)
return httputils.NOT_ALLOWED
# v1: create uuid token with 2x 32 bytes = 256 bit
token = "v1/" + str(base64.urlsafe_b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes), 'utf-8')
@@ -690,9 +713,18 @@ class BaseSharing:
logger.info("Add sharing-by-map: access to path(mapped) %r not allowed for owner %r", PathMapped, Owner)
return httputils.NOT_ALLOWED
if self.permit_create_map is False:
if "m" not in access.permissions:
logger.info("Add sharing-by-map: access to %r not allowed for user %r (permit=False but explicit grant misses 'm')", PathMapped, user)
return httputils.NOT_ALLOWED
else:
if "M" in access.permissions:
logger.info("Add sharing-by-map: access to %r not allowed for user %r (permit=True but denied by 'M')", PathMapped, user)
return httputils.NOT_ALLOWED
access = Access(self._rights, str(User), PathOrToken)
if not access.check("r") and "i" not in access.permissions:
logger.info("Add sharing-by-map: access to path %r not allowed for user %r", PathOrToken, user)
if not access.check("r"):
logger.info("Add sharing-by-map: access to path %r not allowed for user %r", PathOrToken, User)
return httputils.NOT_ALLOWED
# check whether share is already existing as real collection

View File

@@ -185,6 +185,8 @@ class TestSharingApiSanity(BaseTest):
"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": "true",
@@ -272,6 +274,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -458,6 +462,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -613,6 +619,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -800,6 +808,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -892,6 +902,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -1084,6 +1096,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -1183,6 +1197,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -1328,6 +1344,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -1428,6 +1446,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -1596,6 +1616,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -1795,6 +1817,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -1911,6 +1935,8 @@ class TestSharingApiSanity(BaseTest):
"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",
@@ -2048,12 +2074,14 @@ class TestSharingApiSanity(BaseTest):
assert answer_dict['Lines'] == 1
def test_sharing_api_create_conflict(self) -> None:
"""sharing API usage tests related to update."""
"""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",
@@ -2127,3 +2155,340 @@ class TestSharingApiSanity(BaseTest):
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)
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 sharing.INTERNAL_TYPES:
if db_type == "none":
continue
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(/.*)?
permissions: RrWwT
[owner1-t]
user: owner1
collection: {user}/cal-t(/.*)?
permissions: RrWwt
[owner1-M]
user: owner1
collection: {user}/cal-M(/.*)?
permissions: RrWwM
[owner1-m]
user: owner1
collection: {user}/cal-m(/.*)?
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/"
path_owner1_t = "/owner1/cal-t/"
path_owner1_M = "/owner1/cal-M/"
path_owner1_m = "/owner1/cal-m/"
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 sharing.INTERNAL_TYPES:
if db_type == "none":
continue
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 + "dM" + db_type
_, 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 + "dT" + db_type
_, 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 + "dt" + db_type
_, 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 + "dm" + db_type
_, 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")
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 + "eM" + db_type
_, 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 + "eT" + db_type
_, 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 + "et" + db_type
_, 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 + "em" + db_type
_, 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": "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 sharing.INTERNAL_TYPES:
if db_type == "none":
continue
logging.info("\n*** test: %s", db_type)
self.configure({"sharing": {"type": db_type}})
# create map
self.configure({"sharing": {"default_permissions_create_map": "r"}})
json_dict = {}
json_dict['User'] = "user1"
json_dict['PathMapped'] = path_owner1
logging.info("\n*** create map user1/owner1 r -> 200")
json_dict['PathOrToken'] = path_user1 + "r"
_, 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'] == "r"
logging.info("\n*** create map user1/owner1 rw -> 200")
json_dict['PathOrToken'] = path_user1 + "rw"
json_dict['Permissions'] = "rw"
_, 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"
logging.info("\n*** create map user1/owner1 with adjusted default permissions -> 200")
self.configure({"sharing": {"default_permissions_create_map": "RrWw"}})
json_dict['PathOrToken'] = path_user1 + "RrRw"
del json_dict['Permissions']
_, 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"