Merge pull request #2000 from pbiering/feature-sharing-permissions
Feature sharing global and granular permissions
This commit is contained in:
@@ -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
20
config
@@ -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]
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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."""
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user